Setup:
My app uses 2 persistent stores managed by a single persistent store coordinator that is used by several managed object contexts. Both persistent stores store the same entities.
It is required that any inserts, updates and deletes of managed objects are only done in one of both persistent stores, and the app can select the persistent store.
Problem:
In order to store a managed object only in one persistent store, I want to use func assign(_ object: Any, to store: NSPersistentStore)
of a NSManagedObjectContext
. The docs say
Specifies the store in which a newly inserted object will be saved.
Question:
What means „newly inserted“?
willSaveObjectsNotification
to loop though all registeredObjects
of the context and assign the required persistent store?When the app switches the persistent store, I would then reset the contexts so that all managed objects would be fetched newly, and the required persistent store could be set again in the willSaveObjectsNotification
notification.
Is this the right way to go?
Based on Tom Harrington’s answer, I did the following tests.
The result is:
As long as a managed object has never been saved to a persistent store, one can use assign(_:to:)
to define one of several persistent stores into which the object will be saved later.
My code:
// Setup:
// privateStore and sharedStore are persistent stores to which the Item entity is assigned.
// Item is a managed object whose entity is assigned to the persistent stores privateStore and sharedStore.
// It has an attribute "name" to distibguish it from other objects.
// Create for privateStore and sharedStore one fetch request respectively for an Item with name "item"
let namePredicate = NSPredicate(format: "name == %@", "item")
let privateStoreItemFetchRequest = NSFetchRequest<Item>(entityName: Item.entityName)
privateStoreItemFetchRequest.predicate = namePredicate
privateStoreItemFetchRequest.affectedStores = [privateStore]
let sharedStoreItemFetchRequest = NSFetchRequest<Item>(entityName: Item.entityName)
sharedStoreItemFetchRequest.predicate = namePredicate
sharedStoreItemFetchRequest.affectedStores = [sharedStore]
// 1st test: Create an Item without a context, assign it to a store, insert it into a context and try to fetch it from both stores
let item1 = Item(entity: itemEntity, insertInto: nil)
item1.name = "item"
viewContext!.assign(item1, to: privateStore)
viewContext!.insert(item1)
let items1InPrivateStore = try? viewContext!.fetch(privateStoreItemFetchRequest)
print("Test 1: privateStore: \(items1InPrivateStore ?? [])") // Prints item1
let items1InSharedStore = try? viewContext!.fetch(sharedStoreItemFetchRequest)
print("Test 1: sharedStore: \(items1InSharedStore ?? [])") // Prints []
// 2nd test: Create an Item and insert it into a context, assign it to a store and try to fetch it from both stores
let item2 = Item(entity: itemEntity, insertInto: viewContext!)
item2.name = "item"
viewContext!.assign(item2, to: privateStore)
let items2InPrivateStore = try? viewContext!.fetch(privateStoreItemFetchRequest)
print("Test 2: privateStore: \(items2InPrivateStore ?? [])") // Prints item1 and item2
let items2InSharedStore = try? viewContext!.fetch(sharedStoreItemFetchRequest)
print("Test 2: sharedStore: \(items2InSharedStore ?? [])") // Prints []
// Save the context and try to fetch both items from both stores
do {
try viewContext!.save()
} catch { fatalError() }
let items3InPrivateStore = try? viewContext!.fetch(privateStoreItemFetchRequest)
print("Test 3: privateStore: \(items3InPrivateStore ?? [])") // Prints item1 and item2
let items3InSharedStore = try? viewContext!.fetch(sharedStoreItemFetchRequest)
print("Test 3: sharedStore: \(items3InSharedStore ?? [])") // Prints []
// item1 has been assigned above to the privateStore. Try now to assign it to the sharedStore
viewContext!.assign(item1, to: sharedStore) // Run time error: "Can't reassign an object to a different store once it has been saved."