I would appreciate any kind of help here, since I've been working on this for weeks now. I've read through a ton of apple documentations but could solve my problem.
So, I'm building a simple diary app in which the diary pages are saved in Core Data and synced with CloudKit. I have a lot of database operations, which I do asynchronously in the background. For example, when the user writes an entry, I save the diary page every 5 seconds or when the user adds an image to it.
The CloudKit functionality is not used to sync the database with any other devices of the user, but for creating a backup in case the user loses his device. For example, if the user deletes the app and redownloads it, the app automatically checks if the user has existing iCloud data. If this case is true, the user has the option to reset the app or get his old backup back. If the user chooses to restore his old backup, the app will compare the number of local entities in Core Data to the number of entities in iCloud, till this number is equal. This works because CloudKit automatically syncs the local database to the already existing one in iCloud, and downloads all entities in the background.
This is the code I use to enable CloudKit in my app:
class CoreDataService {
// MARK: - Interface
static let shared = CoreDataService()
let cloudKitContainer = CKContainer.default()
private lazy var persistentContainer: NSPersistentCloudKitContainer = {
let persistenceContainer = NSPersistentCloudKitContainer(name: "Database")
guard let containerId = cloudKitContainer.containerIdentifier,
let description = persistenceContainer.persistentStoreDescriptions.first else {
fatalError()
}
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: containerId)
description.cloudKitContainerOptions = options
persistenceContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError()
}
})
return persistenceContainer
}()
}
Recently I have encountered a failure in database integrity. I have found deleted pages. I could not find a pattern to recreate the problem. Therefore, I have the assumption that there is a merge conflict which makes my app delete the pages locally and then redownload them from iCloud. Another error I often see in the console, which I do not understand is:
PFCloudKitExporter exportOperationFinished:withSavedRecords:deletedRecordIDs:operationError:]_block_invoke(492): Failed to delete asset file: file:///var/mobile/Containers/Data/Application/D62B434E-A968-423B-8408-9461A97DA8D1/Library/Application%20Support/ckAssetFiles/3F783A57-2038-45E8-8D93-1DD0A7EBD89F.fxd
Error Domain=NSCocoaErrorDomain Code=4 "“3F783A57-2038-45E8-8D93-1DD0A7EBD89F.fxd” couldn’t be removed." UserInfo={NSUserStringVariant=(
Remove
), NSFilePath=/var/mobile/Containers/Data/Application/D62B434E-A968-423B-8408-9461A97DA8D1/Library/Application Support/ckAssetFiles/3F783A57-2038-45E8-8D93-1DD0A7EBD89F.fxd, NSUnderlyingError=0x281995e90 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
So my questions are:
- Is it that easy to just use an
NSPersistentCloudKitContainerinstead of anNSPersistentContainerto have all iCloud stuff enabled, or do I need more configurations? E.g. do I have to set a merge policy so this merge conflict I described is solved? What does the error mean? - Is my approach of using CloudKit good? I do not use the functionality of syncing between devices. I just want to have a backup of the diary stored in the cloud, so the user will never lose his diary pages, even if his device is stolen. Should I separate CloudKit and Core Data by using a normal a
NSPersistentContainerand create a separate Settings section for backing up user data to the cloud, just like WhatsApp does? I could implement a mechanism to compress all the user's data into one file and upload it to iCloud usingCKRecords. The user could then set a frequency, when the mechanism should push their backup into the cloud. When the user loses his device the backup record is then downloaded and used to restore the old state of the app.