YapDatabase Disappointing CloudKit Example

It’s not often you see code as disappointing as this, particularly because CloudKit is so well designed for the situation being described, but it’s actually very common. I think it happens when you have a developer with speciality in one area, trying to apply it to their understanding of how to implement something in a new area, and just getting the job done as quickly as possible.

I was searching Github for examples of how people are handling CKErrorChangeTokenExpired, and came across this CloudKitManager.m by YapDatabase. In scrolling through I noticed this function that looks to be chaining some CloudKit setup operations together:

- (void)continueCloudKitFlow
{
    DDLogInfo(@"%@ - %@", THIS_FILE, THIS_METHOD);
    
    if (self.needsCreateZone)
    {
        [self createZone];
    }
    else if (self.needsCreateZoneSubscription)
    {
        [self createZoneSubscription];
    }
    else if (self.needsFetchRecordChangesAfterAppLaunch)
    {
        [self fetchRecordChangesAfterAppLaunch];
    }

First thought is, since these methods are usually all async and dependent on one another how can they all be called in line like this? Taking a look at one of these methods gives an ugly surprise! (And I don’t mean them using tabs instead of spaces but yeh that’s ugly too! Had to even fix them just to paste into WordPress.)

- (void)createZone
{
    dispatch_async(setupQueue, ^{ @autoreleasepool {
        
        // Suspend the queue.
        // We will resume it upon completion of the operation.
        // This ensures that there is only one outstanding operation at a time.
        dispatch_suspend(setupQueue);
        
        [self _createZone];
    }});
}

So all of the methods are called one after another instantly, but to make the actual methods (prefixed with underscore) run serially, the developer runs them as blocks on a GCD dispatch queue, and when each block starts it pauses the  queue to prevent the other ones being called. Unbelievable!

The question you are all asking is why on earth didn’t the developer simply make use of CloudKit’s beautiful use of NSOperations and just set up dependencies via an NSOperationQueue to run them serially automatically? I would guess the answer is this particular developer is an expert in GCD (based on the information in the Readme of the project CocoaAsyncSockets), so put simply, they used what they knew to solve the problem.

It’s one of those situations where they got the job done, so that’s great, but I think if everyone takes a moment to learn the NSOperation API properly and see how amazingly well CloudKit builds upon it, it would bring much more enjoyment to developers.

In case you were wondering how it should be done, is the CloudKit operations don’t have to be added to a database or container via addOperation to be processed, a database property can be set on the operations and then it can be added to a custom operation queue, thus allows you to group your operations together, and with dependencies order them serially. It is easy to miss but in the CKDatabase header it says “or schedule operations on your own queue”, that’s the secret.

Leave a Reply