Sqlite with (CloudKit or iCloud Drive) - ios

I have an iOS app that timestamps some events and loads them in UI. To persist the data I use CloudKit which is quite fine and fast enough for my use case.
1st problem is, when an event occures I save it instantly to cloudkit therefore if internet is not reachable I have to handle the situation. I had the idea of having a local sqlite db and sort of keep track of connectivity and deal with syncing manually. Here is where 2nd problem comes in in which any inconsistency of the data must be handled by me (e.g. if the user edits the data while offline).
I found YapDatabase library doing the exact things but I feel the learning curve is a little bit steep.
Another idea could be using sqlite db but saving the file in the icloud drive. This way it will be persisted automatically while I have access to the app sqlite. Any change in the db tables will change the file singnature and fire icloud drive sync.
What's the best choice?

Related

iOS: Documents directory being 'cleaned'

The Situation
I have an app that stores a core data database in the documents directory. It seems to work well for the most part, except for the fact a few users (of a very large number) are complaining their data just 'disappeared'.
It's a carefully/well coded app, no weird errors or crashes coming from Core Data.
My Suspicion
iOS sometimes shows the word 'cleaning' beneath app icons when storage space is low. This cleans some directories to free up space.
Help!
Could this be the cause? If so, how can I stop this? Any light that can be shed on this would be much appreciated.
The documents directory is the recommended place to store a core data database and iOS will never "clean up" anything stored there.
Users can manually delete files in the Documents directory, by uninstalling the app or (if you've enabled it in info.plist) browsing their phone from in iTunes.
Most users do not expect their data to be destroyed when they uninstall an app (Macs and PCs would leave the data in place for example), so this is probably what's happening.
You should consider storing a second copy of the data on your server, or on the user's iCloud account. That way it won't be destroyed by an uninstall. If it's your server, then you can justify charging money for this feature (recurring revenue is good right?).
Backups to iTunes and iCloud will both include your database, so you can instruct users to restore to a recent backup to get their data back.
Also double check your code to see how it handles an out of disk space error when attempting to save changes to the database. Depending how you're using Core Data, this could go bad.
These days Core Data in iCloud or some other cloud solution is the best approach.

CoreData and iCloud; how can I track which device last saved to the cloud?

I have a CoreData app (using https://github.com/lhunath/UbiquityStoreManager), backed by iCloud. In one use case a user with a local store enables iCloud (where data already exists). I want to prompt the user to make a decision of whether to migrate the local data to iCloud or just use the iCloud version. As part of this, I'd like to display the device name and last sync date of the version in iCloud.
I've been tinkering around with my NSPersistentStore's metadata, but that doesn't appear to get synced to iCloud.
Any suggestions?
You could use iCloud's key-value store to store the device name & date of the last sync.
My no doubt unpopular suggestion is "don't". Trying to determine what is in iCloud at any given time puts you on pretty shaky ground. You may be able to get it to work most of the time, but there will always be circumstances where it breaks down.
If you really must import some data when first enabling iCloud, I suggest just always importing the data, and then deduping later as the iCloud data comes in. As ugly as it sounds, that's the only approach really guaranteed to work with Apple's approach.
It is worth taking a look at other Core Data sync frameworks like TICDS and Ensembles. They take a more sane approach to data identity, which means you can avoid the whole deduping step. (Disclosure: I develop Ensembles)
do a metadata query on the iCloud files and check the most recent transaction log file in iCloud. See the link below for a sample app that uses this approach to check whether the app is properly synchronised with iCloud.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/
EDIT:
I just realised I don't get the actual device name, but once you have found the most recent log file then use this to get the device. Just be aware this call may be expensive.
NSFileVersion *openedVersion = [NSFileVersion currentVersionOfItemAtURL:fileURL];
return openedVersion.localizedNameOfSavingComputer;

Duplicating CoreData records with iCloud sync

When core data syncs with iCloud, occasionally all of my records are doubled. I believe this happens on reinstall, where the app has not yet synced data, creates a new data set, and then iCloud syncs and there are double of each record for each model.
Is there a way to prevent this sort of behavior? Right now I'm checking every model on load and data sync for duplication, but this seems messy and hackish.
iCloud transfers data between devices asynchronously, and there can be quite some time between when you add data to the iCloud container, and when it actually gets transferred. Metadata is transferred between devices faster, but even this can be quite delayed.
The reason this is important is that you can never do a test on one device that will guarantee that data has not already been added to iCloud. One device may have seeded data, but not yet begun to upload its files/metadata. At that point, a second device cannot know about the seeded data from the first device.
Apple's advice is to handle this by de-deplicating your data after each merge. Each device just adds its own seed data, and if you find that it has been added twice, you delete half of it, being careful to ensure corresponding objects are deleted on each device.
This approach works, but feels a bit like a hack. An alternative, used in my Ensembles framework, is to supply global identifiers for your objects so that the sync framework can automatically import and merge seed data. That is the approach taken in Ensembles, and it makes seeding data quite a bit easier and less ad hoc.
Trying to use just an iCloud store is folly. Data must be stored both locally and in iCloud, or you will run into serious problems.
Use MagicalRecord if you want core data.

iOS, Core Data and iCloud - switching context

I am creating an app where data needs to be displayed right away from the local datastore. I fire off a background thread when the app starts to determine if iCloud is available.
I looked everywhere and can't find the solution to this: when iCloud becomes available, can I change the "options" on the persistentStore to start using iCloud transactions?
I'm not sure what the proper approach is in this situation. Everything I try causes the application to crash.
Originally I had it so the iCloud checking wasn't in a background thread and the app worked fine, but occasionally timed out.
You have not to know when iCloud becomes available. You just work with data but you don't send them directly to iCloud. iOS does it instead you. So only it knows when and how it should send data.
No, you can't change the options on an NSPersistentStore object once it exists. You can only specify the options when adding the persistent store to the NSPersistentStoreCoordinator. The closest you could get to changing options would be to tear down the entire Core Data stack and start over with different options.
That wouldn't help, though, because:
Even if you have detected that iCloud is available (I'm guessing using NSFileManager, either via its ubiquityIdentityToken or by calling URLForUbiquityContainerIdentifier:), your call to addPersistentStoreWithType:configuration:URL:options:error: might still block for a while. If there's new data available in iCloud, it doesn't start downloading until you add the persistent store, and that method doesn't return until the download process is finished. And sometimes, iCloud just makes that method block for a while for no readily apparent reason.
If you let the user make any changes to the data while using non-iCloud options, those changes will not get automatically sent to the cloud later. Core Data only sends changes to iCloud when the data changes while iCloud is active-- which makes it generate a transaction. You'd have to load and re-save any changes the user made, or those changes would never make it to the cloud.
You have, unfortunately, hit on one of the major stumbling points when using Core Data with iCloud. You can't make the full data store available until Core Data finishes communicating with iCloud-- because your call to add the persistent store doesn't return until then. And you can't do anything to speed up that process. This is just one of the headaches you'll run into if you continue trying to use iCloud with Core Data.
Depending on the nature of your data you might be able to use two data stores, one purely local and one synced via iCloud. You could make the purely local data store available while the iCloud one tries to get its act together well enough to be useful. If you stick with one data store though, you're stuck with the delay.

How can I enable iCloud synchronization for my Core Data database?

I want to make my app sync its SQLite Core Data store between devices using iCloud. Right now I let users manually sync their SQLite files using Dropbox, but that is cumbersome for users. I want it to be automatic.
What is the best practice to achieve this functionality? Do I need any special provisioning profiles?
I read somewhere that I need to set up my Core Data database to use UIManagedDocument but I haven't found anywhere that explains how to do this clearly. Any suggestions?
Turn on cloud syncing. That is step one. From there it depends on your application and its requirements.
Sorry for the short answer but your question is rather vague and you do not have any requirements for your app.
Assuming it is just a sqlite file all you need to do is turn on cloud syncing, change your Core Data stack to handle being async and Bob's your uncle.
Update
It is unlikely you will need to use UIManagedDocument. I would say that unless you have a kitchen sync type application you are fine using the Core Data ubiqutous store syncing at the NSPersistentStore level.
At this time, quite a few people are having issues with UIManagedDocument so I would avoid it unless you absolutely need to sync things that are not contained within the sqlite file.

Resources