I want to have my CoreData to be synced to iCloud which works perfectly right now. But when I turn off iCloud sync in the simulator all of the data is "lost". But when I turn on iCloud sync again the data comes right back which is nice. But I want the data to be stored locally as well, if the user doesn't want to fill up his iCloud storage.
The scenario being: the user sees a lot of data (which will never be GB of data but maybe some users are picky even with MB of data) fill up his iCloud and want the sync turned of for my app. When the sync is turned of all of the user's data disappears locally, but is still in the cloud. I want the data not to disappear when the sync is turned of.
Is my "request" possible?
This is my code:
import CoreData
class DataController: ObservableObject {
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "CoreData")
let address = Bundle.main.path(forResource: "CoreData", ofType: ".momd")
// Create a store description for a local store
let localStoreLocation = URL(fileURLWithPath: "\(address!)/Local.sqlite")
let localStoreDescription =
NSPersistentStoreDescription(url: localStoreLocation)
localStoreDescription.configuration = "Local"
// Create a store description for a CloudKit-backed local store
let cloudStoreLocation = URL(fileURLWithPath: "\(address!)/Cloud.sqlite")
let cloudStoreDescription =
NSPersistentStoreDescription(url: cloudStoreLocation)
cloudStoreDescription.configuration = "Cloud"
// Set the container options on the cloud store
cloudStoreDescription.cloudKitContainerOptions =
NSPersistentCloudKitContainerOptions(
containerIdentifier: "iCloud.com.my.identifier")
// Update the container's list of store descriptions
container.persistentStoreDescriptions = [
cloudStoreDescription,
localStoreDescription
]
// Load both stores
container.loadPersistentStores { storeDescription, error in
guard error == nil else {
fatalError("Could not load persistent stores. \(error!)")
}
}
return container
}()
init() {
persistentContainer.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
}
}
You have two persistent store descriptions, but that doesn't mean that when you save an object that it goes into both persistent stores. Your code would need to do that.
A managed object belongs to only one persistent store. If you have more than one store (as in your code), what happens? Since you have multiple persistent stores where an object could belong to either one, it goes into the first one in the list-- which is why they're all cloud objects here.
If you want to save the same data in both stores, you would need to do something like this:
NSManagedObjectContext
's function assign(_ object: Any, to store: NSPersistentStore)
to tell it to put the duplicate into the right store.NSFetchRequest
's property affectedStores
to make sure you only fetch from one of the two stores. Otherwise you'll have duplicate results, since you would have results from both stores.Since the duplicate is a different object instance, it will have a different NSManagedObjectID
. You would probably need to add your own unique ID field so you can match an object from one store to the corresponding one from the other store.
You'll need to do similar things any time you change a managed object. If you change one, find the duplicate from the other store and make the same change.
If this sounds complicated, it is. This is not a simple problem to attack. It's not impossible but it isn't going to be easy to get it right.
[If you were using different model configurations you could have different managed object types go into specific stores. Each object would still go into a single store, not both, but the choice would be automatic. This doesn't help solve your problem but the docs for the assign
method mention this situation so I thought I'd mention it too.]