I’ve been trying to make use of iCloud in an iOS app over the last couple of weeks, and it’s been absolutely infuriating. There’s a large volume of documentation, but it’s not very good, and it’s incomplete. So let’s run down the laundry list:
Entitlements
Getting the entitlements to work is, in general, an incredible pain in the arse. There’s very little in the way of debugging tools, and the error messages you get from various libraries, tools and websites are obtuse to the point of being worse than useless.
I was relatively lucky, in that I had only minor issues regarding randomly bogus provisioning profiles and so forth – nothing that couldn’t be fixed by the iOS SDK equivalent of “reinstall Windows and reboot”. But it’s just depressing that this kind of stuff still doesn’t work properly, after all this time and with tens of thousands – hundreds of thousands? – of active developers all reminding Apple how shit it is.
It makes me miss working at Apple, where I could just disable code signing entirely on my development devices. I suppose I could still do so via some jailbreaking goodness, but that ultimately only defers the pain until it comes time to submit to the App Store.
The Simulator
iCloud doesn’t work in the simulator. Flat out doesn’t work. But it doesn’t fail with a meaningful error log or exception. Oh no. Nor is this complete lack of functionality documented anywhere that I saw. I’ve seen it mentioned since – almost exclusively on 3rd party walk-throughs that try to guide you around the landmines, such as this. Le sigh.
Core Data vs iCloud
Core Data supports iCloud intrinsically. Only not always. The docs tell you to specify NSPersistentStoreUbiquitousContentNameKey
in the persistent store options, because “This option is required for ubiquitous content to function”. Only that’s a half-truth. It’s required for SQLite based stores to function. If you specify it for a binary store, Core Data will simply explode after you create your document, with the exception:
-[NSBinaryObjectStore connection]
: unrecognized selector sent to instance 0xfoo
Nothing meaningful, no explanatory error message, no “oh, hey silly, you can’t use this data store with that attribute”. No, it just tries blindly and explodes.
So I guess you’re supposed to just handle the iCloud stuff completely manually, as for a generic UIDocument
. I really do just guess – there’s no documentation at all dealing with binary stores explicitly, and as noted what deals with generic Core Data + iCloud is just wrong.
So thanks for nothing, docs.
It’d also be nice if there were an example of how you’re supposed to do that – enable ubiquity for the store manually – since you’re creating a new UIManagedDocument at an explicit location, and then you’re, what, going to move it out from under itself into the ubiquitous documents folder? Or do you close the document right after creation, move it, and reopen it? How awkward, if so. It seems to me that this was not planned very well at all.
It’s also frustrating that the sync semantics – transactional or whole-database – are coupled with the store format to begin with. I need whole-database granularity – it wouldn’t make any sense at all to merge random sets of transactions – but I could make use of the SQLite store for various things. It also evidently is the only really supported path, at least insofar as the documentation is concerned, so it would have saved me a lot of pain.
For example, it’s a royal pain in the arse to even figure out how to use the binary store to begin with:
Binary Stores
You’d think it would be a pretty trivial exercise to change the persistent store type from the default, SQLite, to the binary format. Obviously you’d specify the format when creating the persistent store. Wait, no, apparently not. Hmm… to the interwebs! Ah, so it’s specified as part of the file type info in your project’s settings. Hmm.. wait, nope, not there anymore. Heck, let’s just add the NSPersistentStoreTypeKey anyway, see what happens. Nothing. Awesome.
As it turns out, you have to subclass UIManagedDocument
and override persistentStoreTypeForFileType:
. I really don’t like any kind of object framework that relies on subclassing. Cocoa has generally been pretty good at avoiding this. It boggles my mind why UIManagedDocument
‘s so poorly designed. And why it’s basically undocumented. Le god damn sigh.
NSMetadataQuery
NSMetadataQuery
is infamously flakey. I knew that before even using it, from the sheer volume of complaints I’ve seen about it over the years. So I was cautious – I only copy-pasted verbatim from the examples, not daring to change a thing, not even method names.
And it doesn’t work out of the box, even from the code examples. #@%!
Long story short, NSMetadataQueryDidUpdateNotification
does not do what you might expect. Certainly not what I expected. I read the documentation as saying that it would be used to deliver results, and NSMetadataQueryDidFinishGatheringNotification
would be posted when the full first pass had completed. That’s a very typical pattern used elsewhere in Apple’s APIs (including Spotlight, so one would assume NSMetadataQuery
would work the same).
But it doesn’t. Instead, all of the first full run’s results are buffered up and provided by NSMetadataQueryDidFinishGatheringNotification
. NSMetadataQueryDidUpdateNotification
is purely for subsequent, real-time changes. That’s also a valid design, albeit unusual, suboptimal and not bloody documented as such. It almost defeats the purpose of an asynchronous API if you delay returning any results until the end anyway.
And it’s not even just Apple’s documentation that gets this wrong – even otherwise good tutorials won’t work as written, because they don’t even register for the NSMetadataQueryDidFinishGatheringNotification
. Perhaps the behaviour changed at some point (hell, perhaps it’s different between the simulator and devices… hell’s bells…).
…
In summary, I’m really quite annoyed at the state of the iOS SDK, both design and documentation. I really miss being able to look at the source for everything. Only in hindsight do I realise how much more bearable that made everyday coding. I also miss the more mature Cocoa frameworks, that might have some warts but are generally well designed and have stood the test of time. There was a lot of good work ignored when the iOS frameworks were Frankensteined into existence.
This is an extremely helpful bit of information. The explanation of NSMetadataQueryDidFinishGatheringNotification and NSMetadataQueryDidUpdateNotification made a huge difference in my wrestling match with iCloud in my app. Thank you for putting the effort into sharing this.
You’re most welcome.