CKRecordZoneSubscription Notes

If you attempt to set desiredKeys on a notificationInfo for the new iOS 10 CKRecordZoneSubscription class, saving results in the runtime error:

<CKError 0x17424b610: "Invalid Arguments" (12/2006); server message = "cannot add additionalFields to this subscription type"; uuid = 4E08C616-97AA-4E9A-B584-B7972B1CD99B; container ID = "iCloud.com.x">

Which makes sense given the notification is about a zone changing and not a record, however one might assume it can send the record that was responsible for the change but looking to CKRecordZoneNotification shows no record properties are available. It’s always kind of disappointing to see subclassing not working out, i.e. where the subclass is denied features of the parent, it can sometimes point to bad class design however in this case Apple could have handled it better either by client side validation or at least a note in the header. It’s extra disappointing given in iOS 10 they refactored from flat CKSubscription inits for the different subscription types (which did have their own limitations, e.g. initWithZoneID only allowed zero for options) to subclasses. I hope they’ve thought this new design through because if it is refactored again it could be a real headache for framework backwards compatibility.

 

CloudKit Syncing References

Here are some references from around the web on how to implement CloudKit syncing. It will be added to…

As I Learn CloudKit Syncing by Eric Allam (added 5th January 2017)

Probably my favourite tutorial so far, Part 1 follows the example given in the Advanced CloudKit talk from 2014, unfortunately the talk and this tutorial doesn’t get as far as using push notifications via a CKRecordZoneSubscription and resorts to fetch only. It correctly uses CKFetchRecordChangesOperation for this, however it contains a mistake in part 4. It says that only changed properties are included in the CKRecord given by the recordChangedBlock, in fact the full record is included by default, and it now also supports a desiredKeys in the newer version of this class named CKFetchRecordZoneChangesOperation. It correctly identifies using NSOperation dependencies as the right approach for queuing related operations, however in seek of queue error handling Eric looks to the Advanced NSOperations talk and corresponding open source project, which unfortunately is of a different design to CloudKit NSOperations and he hits some problems.

Seam – Seamless CloudKit Sync with CoreData (added 5th January 2017)

This project provides tight coupling with CoreData that seems to go against Apple’s designs of keeping frameworks more loose. For example, it wraps conflict handling which might be something the developer requires full control over. It’s always the same with wrapping, you end up hiding away something needed and then having to write more code to expose it again. Opting certain entities out of syncing requires extra effort, and it appears to support only an all-or-nothing sync, rather than say just up or down. The project was coded in Swift so unfortunately fell victim to the constant syntax changes of the language, and has been left broken on Swift 3.0. Furthermore, NSOperation is designed around returning errors rather than exceptions that Swift uses, I’m not sure how well the developer handles this but it does seem a potential area of weakness.

CloudKit + Core Data + NSOperations – Syncing by Nick Harris (added 5th January 2017)

This is the top tutorial on Google for CloudKit syncing, but sadly has mistakes and leaves many unanswered questions. Uniquely, Nick uses a record zone per record type which obviously isn’t the right approach for these kind of records, he realises this later, and the reason is that CKRecordZoneSubscription can be scoped to a recordType. Another mistake is by subclassing CKFetchRecordsOperation to perform processing, however he strangely does then use correctly use NSOperation dependencies for the sync method. One interesting approach is to use a separate entity to store deletes, so that the real entity can be deleted as normal rather than just soft deleted; this has the advantage that predicates don’t need the additional where status != deleted.  There doesn’t appear to be any error handling if any one of the operations fails, furthermore the synchronous processing NSOperation just fatal errors if anything goes wrong with the Core Data method calls. I believe if he had access to an asynchronous NSOperationQueue class that supports errors and cancellation it would have helped a lot. Written in Swift so again falls victim to syntax changes.

Apple CloudKit Engineer on Stack Overflow (added 5th January 2017)

A developer named farktronix gives great insight into the inner workings of CKFetchRecordZoneChangesOperation on the server. From the name of the class you might think that this returns all changes or deletes, which at first glance would appear far too much data to be useful for an initial sync, so for example, you might think instead to query to get the first set. However the developer states that the server coalesces changes before sending them (which gives the bug being discussed), this means that if a record was created and then deleted, the server is smart enough not to send either of those events in the fetch changes operation. This is a really great piece of insider info, and hopefully this user shares more in the future.

Enabling iOS 9.3 SDK in Xcode 8 for iOS 10

Some of the new iOS 10 features contain link-time checks, that is they are only enabled if the app was linked to the iOS 10 SDK. When working on an open source library it is necessary to debug when linked to an older SDK, e.g. iOS 9.3, on a newer device iOS, e.g. iOS 10. It currently isn’t possible to deploy to a newer iOS device with the old Xcode designed for the older SDK. For this reason it can be necessary to enable the older SDK in the newer Xcode, which can be done as follows:

Note: these commands assume Xcode 7 is at \Applications\Xcode.app and Xcode 8 is at \Applications\Xcode-beta.app

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.3.sdk

sudo /usr/libexec/PlistBuddy -c "Set :MinimumSDKVersion 9.3" /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Info.plist 

sudo /usr/libexec/PlistBuddy -c "Set :MinimumSDKVersion 9.3" /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Info.plist

After running these commands, restart Xcode 8. Now you can set the Base SDK to iOS 9.3 and deploy to an iOS 10 device.

Any newer code generation features in Xcode 8 will need to be turned off, e.g. a Core Data model will need to have code generation set to none.

Welcome to my Developer Blog

I thought it would be good to start this developer blog with some photos of how my iOS development began. At university during my Ph.D. I had been building .NET Compact Framework apps in C# for Pocket PCs, usually on iPaqs with GPS sleeves and using Wi-Fi.

A bunch of iPaqs and I – 5th July 2004

Then in 2007 the iPhone came out. That summer, during my job at Microsoft as a lowly intern we bought iPhones for “research”.

Me at Microsoft – 25th July 2007

Given it was initially a US only product and SIM locked to AT&T I followed Geohot’s live blog on jailbreaking for purposes of SIM unlocking. We used Microsoft’s micro-soldering equipment to attach the wires for the hardware jailbreak, then performed the SIM unlock and then I could begin using the iPhone on a network the UK, must have been one of the first!

My first iPhone open with the jailbreaking wires attached – 25th August 2007

Microsoft was a fantastic experience but I also learned that I never wanted to work at a corporation again. So from there I began reverse engineering the frameworks and building custom apps and even distributing them via Installer that was released that same summer, and then the following year, Cydia. My first jailbreak apps were primarily around Wi-Fi scanning – my own app WiFiFoFum and a Wi-Fi positioning system for Navizon. Apple picked up on this jailbreak app phenomenon and subsequently released their public SDK and App Store, at which point WiFiFoFum was almost already to go and was one of the first apps in the store. Everything was going great with thousands of sales per day until unfortunately Apple decided to ban it for using private APIs. They still to this day do not allow Wi-Fi scanners in the store.

Since then I have been perfecting my skills, researching everything about iOS, Macs, Cocoa and Objective-C, learning all the new technologies as they become available. I develop my own apps and ones for academic research projects and the odd bit of consultancy. I’m still very interested in Wi-Fi but more recently I have developed an interest in realtime syncing of information between devices and apps for health. Hopefully you will find any information on this blog useful.