Last active
November 30, 2017 06:08
-
-
Save jazzedge/021b7330ffc98dc7b49e80896ff4e45b to your computer and use it in GitHub Desktop.
Saving records is, perhaps, the most complicated operation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| See: https://www.whatmatrix.com/blog/a-guide-to-cloudkit-how-to-sync-user-data-across-ios-devices/ | |
| Saving records is, perhaps, the most complicated operation. The simple act of writing a record to the database is straightforward enough, but in my example, with multiple clients, this is where you’ll face the potential issue of handling a conflict when multiple clients attempt to write to the server concurrently. Thankfully, CloudKit is explicitly designed to handle this condition. It rejects specific requests with enough error context in the response to allow each client to make a local, enlightened decision about how to resolve the conflict. | |
| Although this adds complexity to the client, it’s ultimately a far better solution than having Apple come up with one of a few server-side mechanisms for conflict resolution. | |
| The app designer is always in the best position to define rules for these situations, which can include everything from context-aware automatic merging to user-directed resolution instructions. I am not going to get very fancy in my example; I am using the modified field to declare that the most recent update wins. This might not always be the best outcome for professional apps, but it’s not bad for a first rule and, for this purpose, serves to illustrate the mechanism by which CloudKit passes conflict information back to the client | |
| Note that, in my example application, this conflict resolution step happens in the CloudKitNote class, described later. | |
| // Save a record to the iCloud database | |
| public func saveRecord(record: CKRecord, completion: @escaping (Error?) -> Void) { | |
| let operation = CKModifyRecordsOperation(recordsToSave: [record], recordIDsToDelete: []) | |
| operation.modifyRecordsCompletionBlock = { _, _, error in | |
| guard error == nil else { | |
| guard let ckerror = error as? CKError else { | |
| completion(error) | |
| return | |
| } | |
| guard ckerror.isZoneNotFound() else { | |
| completion(error) | |
| return | |
| } | |
| // ZoneNotFound is the one error we can reasonably expect & handle here, since | |
| // the zone isn't created automatically for us until we've saved one record. | |
| // create the zone and, if successful, try again | |
| self.createZone() { error in | |
| guard error == nil else { | |
| completion(error) | |
| return | |
| } | |
| self.saveRecord(record: record, completion: completion) | |
| } | |
| return | |
| } | |
| // Lazy save the subscription upon first record write | |
| // (saveSubscription is internally defensive against trying to save it more than once) | |
| self.saveSubscription() | |
| completion(nil) | |
| } | |
| operation.qualityOfService = .utility | |
| let container = CKContainer.default() | |
| let db = container.privateCloudDatabase | |
| db.add(operation) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment