Sunday, April 2, 2023
HomeiOS Developmentios - Cannot save objects with references outside their own stores, background...

ios – Cannot save objects with references outside their own stores, background and main context issue

[ad_1]

I have a core data store. What I want is to perform saves in a background context, and fetches on the main context. So I have the following setup of CoreDataStack that handles the saving/fetching in the core data:

class CoreDataStack {
  static let modelName = "Model"

  static let managedObject: NSManagedObjectModel = {
    guard let modelUrl = Bundle.module.url(
      forResource: "Model",
      withExtension: "momd"
    ) else {
      return .init()
    }
    return NSManagedObjectModel(contentsOf: modelUrl) ?? .init()
  }()

  lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(
      name: Self.modelName,
      managedObjectModel: Self.managedObject
    )

    let dispatchGroup = DispatchGroup()
    dispatchGroup.enter()

    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
      if let error = error as NSError? {
        print("Unresolved error \(error), \(error.userInfo)")
      }
      dispatchGroup.leave()
    })

    dispatchGroup.wait()

    return container
  }()

  lazy var backgroundContext: NSManagedObjectContext = {
    let context = persistentContainer.newBackgroundContext()
    context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    return context
  }()

  lazy var mainContext: NSManagedObjectContext = {
    let mainContext = persistentContainer.viewContext
    mainContext.automaticallyMergesChangesFromParent = true
    mainContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
    return mainContext
  }()

  func fetch<Item: NSManagedObject>(
    _ request: NSFetchRequest<Item>
  ) -> [Item] {
    do {
      let items = try self.mainContext.fetch(request)
      return items
    } catch let error as NSError {
      print("Unresolved error \(error), \(error.userInfo)")
      return []
    }
  }

  func saveContext() {
    backgroundContext.perform {
      do {
        try self.backgroundContext.save()
      } catch let error as NSError {
        print("Unresolved error \(error), \(error.userInfo)")
      }
    }
  }
}

I have an NSManagedObject called ManagedBar. This contains a data property which is an NSSOrderedSet of another NSManagedObject called ManagedFoo.

I have a function saveFoo(_:) which creates a ManagedFoo on the background context, requests a ManagedBar in the foreground context, then if that ManagedBar exists, adds it to the ManagedFoo to data. Then the background context is saved:

func saveFoo(_ Foo: Foo) {
  let managedFoo = ManagedFoo(
    context: coreDataStack.backgroundContext,
    name: foo.name,
  )

  let request = ManagedBar.fetchRequest()
  request.predicate = NSPredicate(format: "name == %@", foo.name)

  if let bar = coreDataStack.fetch(request).first {
    bar.addToData(managedFoo)
  } else {
    let event = ManagedBar(
      context: self.coreDataStack.backgroundContext,
      name: foo.name
    )
    self.coreDataStack.saveContext()
  }
}

I also have a function to retrieve all the name properties of ManagedBar which fetches them all from the main context:

func getAllBarNames() -> [String] {
  let fetchRequest = ManagedEvent.fetchRequest()
  let eventData = coreDataStack.fetch(fetchRequest)
  let names = eventData.map { $0.name }
  return names
}

Then, when I run the following code, it works most of the time but sometimes fails:

func testGetAllEventNames() {
  let foo1 = Foo()
  saveFoo(foo1)

  let foo2 = Foo()
  saveFoo(foo2)


  let names = getAllBarNames()
  print(names)
}

This sometimes works fine, but other times fails for one of two reasons:

  1. Cannot save objects with references outside their own stores.
  2. Collection <__NSCFSet: 0x6000031407b0> was mutated while being enumerated. (this occurs on the second saveFoo)

Other times, names doesn’t contain all the names that I’ve saved. These errors happen even if I run this code in a unit test and wait for the NSManagedObjectContextDidSave notification before calling getAllBarNames()

This must be caused by some background/main context issue but not entirely sure what. Does anyone know what’s going wrong here?

[ad_2]

Source link

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments