CloudKit Sync Nightmare
So after previous post on analysing the web requests, where we found out the classes involved and sort of when they should be used, we are still clueless as to how to structure our app to enable CloudKit syncing. We know we shouldn’t subclass fetch operation but then how should we gather our information to pass to the operation? Should we create our own operation that then start the fetch operation within? Or use queues, or dependencies? In general should we use our own queues or always use the container database’s built in queue? How should we structure our retry logic? Does NSURLSession’s discretionary mode handle CloudKit retry or just network failure retry? Should be be using longlife operations for sync? Should we be queuing or coalescing fetches between multiple pushes and between anything background and anything user like pull-to-refresh? So many unanswered questions. What a nightmare, Apple has been absolutely useless in their code samples at the WWDC talk (e.g. 16m 48s not useful example and didn’t even bother to demonstrate record sync). And what the hell do they mean by the requests sent last 24 hours / 7 days stuff? I think I need to class-dump the headers to figure out how Apple are doing this. So turns out the app uses an embedded framework for the CloudKit code, here is the dump of NewsCore on my Github, let’s take a look.
First of all we notice 336 files. 336 files just for the networking and caching, not the UI. Unbelievable, but lets carry on anyway.
A lot of the CloudKit related files are prefixed with FCCK, 27 of them! And there are many more like controllers etc. that use these objects. Maybe half the files have a reference to some aspect of CloudKit one way or another.
FCAppConfiguration looks like a possible entry point for this framework, it contains a FCCloudContext which looks like the heart of the CloudKit stuff, with many controllers, centres, managers and queues. Looks like it stores the controllers for syncing the reading list, FCReadingList which is a subclass of FCPersonalizationData : FCPrivateZoneController.
From FCModifyRecordsCommand we notice the coalesceWithCommand method so they are coalescing commands, this might eventually prevent duplicate network requests for the same thing, but doing it at the command level rather than the network level.
The operation subclass hierarchy is as follows FCCKFetchRecordChangesOperation : FCCKOperation : FCOperation : NSOperation
FCOperation isAsynchronous (no other reason for implementing that method) and childOperations suggests they are implementing operation grouping not with sub-queues but instead with dependencies between operations and tracking the group using an array, which would allow the group to be cancelled after being added to more global queue.
FCCKFetchRecordChangesOperation is like a wrapper around CKFetchRecordChangesOperation. Since that class isn’t an ivar anywhere it suggests its declared locally, which means they didn’t need to deal with self retain cycle block bugs. It has properties for everything needed to perform a fetch changes operation including: database, zone, desiredKeys, allChangedRecordsByID and allDeletedRecordIDs. It even has _continueFetchingRecords and resetForRetry methods which makes it look like the operation is being retried from within this operation. Is it added to the database queue and then addChildOperations from FCOperation though?
FCCancelHandler is an interesting class that likely is for wrapping the operation object into something that is safe for a UI developer to work with. It doesn’t appear to be used for CloudKit operations though.
FCAsyncSerialQueue shows they are using serial queues however it isn’t obvious if they are implemented via addDependency on last or with maxConcurrentOperations = 1. It is used in the FCCKRecordZoneManager.
FCPushNotificationCenter is the place where push notifications are managed.
They have added some convenience methods via FCCKDatabase+Additions.h which have completion handlers but no cancellation object returned.
Like we saw in the flat binary filenames used for caching modifications to be uploaded they are using the word command in the class names, e.g. FCModifyReadingListCommand and FCCommandQueue looks to be for managing the binary file with the loadFromDisk and serializeCommands methods.
FCCKTestDatabase makes me think they are taking advantage of dependency injection for testing purposes, where they can pass in a mocked cloud database for testing instead of doing real network requests.
To be honest I wasn’t expecting this complicated an app, this looks like at least a year of coding, maybe even more. Remember the app only syncs a few record types, reading list (bookmarks), history (read articles) and personalisation data like favourite feeds. Implementing syncing is going to be no easy task at all; no wonder we are all clueless after a few hours of WWDC talks! Hopefully some of my research has helped you though.
Sorry I didn’t link every class name, WordPress’s post editor stopped letting me set links for some reason.