OK guys, I think I got it working.
UPDATE Initially it worked for me but I had some very strange effects and the fact that if you log out from iCloud the ubiquity container gets deleted made me think about alternative solution. Now I use Ensembles library and am mostly very satisfied with it. END UPDATE
I just pimped my own test app I wrote 2 years ago, that had no CoreData nor iCloud and it seems to work nice. It syncs the database across my iPhone and iPhone Simulator. The one little thing that drives me nuts is that I still don't see anything on https://developer.icloud.com ..
Now I'm going to do the same for my distributed app. Here is exactly what I did:
- The nice thing about all that stuff is that you almost don't have to do nothing on the Developer Portal (like create and download provisioning profile, even creating App ID is not necessary) - XCode 6.3.2 does all this for you; if you know the order, of course. So first, go to your target's Settings -> Capabilities, and enable iCloud. Xcode does the job of adding Entitlements file, creating App ID on the developer portal and creating iCloud Container. Uncheck "Key-Value Storage" and check "iCloud Documents".
- I assume you are using MagicalRecord and you setup your Core Data stack as following:
[MagicalRecord setupAutoMigratingCoreDataStack];
In this case MagicalRecord creates a local store which name you can get by calling [MagicalRecord defaultStoreName]
. You will need to state this one in the iCloud setup call. So the code I have right now in AppDelegate.m is:
NSString *defaultStoreName = [MagicalRecord defaultStoreName];
[MagicalRecord setupCoreDataStackWithiCloudContainer:@"Container ID from Developer Portal (as on image)"
contentNameKey:@"DataStorage" // It seems like you can write whatever you want here
localStoreNamed:defaultStoreName // Unless not specifically named earlier
cloudStorePathComponent:@"stuff"]; // Seems like you can write whatever stuff you want.
This method worked for me. The other, shorter iCloud setup calling method (without ContentNameKey) was throwing no exceptions, but didn't work properly. I guess, you just have to state ContentNameKey.
Here is where you get container ID on Developer Portal. I know, it seems like a very detailed instructions, but I wish it was so clear to me that "iCloudContainer" string in the function above is actually iCloudContainerID and should be retrieved from Developer Portal.
- Go to device Settings -> iCloud -> iCloud Drive and enable it. Login to iCloud with your account. This is crucial for the whole thing to work.
Now try to run the app on the simulator at least. If you've got no "iCloud not enabled" message in the logs, you're on the half-way.
Register the object that is responsible for all Core Data actions to listen to the NotificationCenter
on following events:
NSPersistentStoreCoordinatorStoresWillChangeNotification
NSPersistentStoreCoordinatorStoresDidChangeNotification
NSPersistentStoreDidImportUbiquitousContentChangesNotification
like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(storesWillChange:)
name:NSPersistentStoreCoordinatorStoresWillChangeNotification
object:nil];
and handle the changes properly.
The hard job is still yet to be done and now it depends on your project and I can not give you any advice individually.
For me the problems were:
- how to react on changes in the remote store
- how to merge different data and avoid duplicates
- how to avoid complete data loss if iCloud container is empty
But I hope to find all the answers to my questions in the official Apple Manual to iCloud.
I'm happy to having avoided a couple of days of headache for you! Please write a comment if this solution worked for your project!