Syncing a local sqlite file to iCloud - ios

I store some data in my iOS app directly in a local .sqlite file.  I chose to do this instead of CoreData because the data will need to be compatible with non-Apple platforms.
Now, I'm trying to come up with the best way to sync this file over iCloud.  I know you can't sync it directly, for many reasons.  I know CoreData is able to sync its DBs, but even ignoring that using CD would essentially lock this file into Apple platforms (I think? I've only looked into CD a bit), I need the iCloud syncing of this file to work across ALL of iCloud's supported platforms - which is supposed to include Windows.  I have to assume that there won't be any compatibility for the CoreData files in the Windows API.  Planning out the best way to accomplish this would be a lot easier if Apple would tell us any more than "There will be a Windows API [eventually?]"
In addition, I'll eventually need to implement at least one more sync service to support platforms that iCloud does not.  It would be helpful, though not required, if the method I use for iCloud can be mostly reused for future services.
For these reasons, I don't think CoreData can help me with this.  Am I correct in thinking this?
Moving on from there, I need to devise an algorithm for this, or find an existing one or an existing 3rd party solution.  I haven't stumbled across anything yet. However, I have been mulling over a couple possible methods I could implement:
Method 1:
Do something similar to how CoreData syncs sqlite DBs: send "transaction logs" to iCloud instead and build each local sqlite file off of those.
I'm thinking each device would send a (uniquely named) text file listing all the sql commands that that device executed, with timestamps.  The device would store how far along in each list of commands it has executed, and continue from that point each time the file is updated. If it received updates to multiple log files at once, it would execute each command in timestamp order.
Things could get 'interesting' efficiency-wise once these files get large, but it seems like a solvable problem.  
Method 2:
Periodically sync a copy of the working database to iCloud.  Have a modification timestamp field in every record.  When an updated copy of the DB comes through, query all the records with newer timestamps than some reference time and update the record in the local DB from the new data.
I see many potential problems with this method:
-Have to implement something further to recognize record deletion.
-The DB file could get conflicts. It might be possible to deal with them by handling each conflict version in timestamp order.
-Determining the date to check each update from could be tricky, as it depends on which device the update is coming from.
There are a lot of potential problems with method 2, but method 1 seems doable to me...
Does anyone have any suggestions as to what might be the best course of action? Any better ideas than my "Method 1" (or reasons why it wouldn't work)?

Try those two solutions from Ray Wenderlich:
Exporting/Importing data through mail:
http://www.raywenderlich.com/1980/how-to-import-and-export-app-data-via-email-in-your-ios-app
File Sharing with iTunes:
http://www.raywenderlich.com/1948/how-integrate-itunes-file-sharing-with-your-ios-app
I found it quite complex but helped me a lot.

Both method 1 and method 2 seem doable. Perhaps a combination of the two in fact - use iCloud to send a separate database file that is a subset of data - i.e. just changed items. Or maybe another file format instead of sqlite db - XML/JSON/CSV etc.
Another alternative is to do it outside of iCloud - i.e. a simple custom web service for syncing. So each change gets submitted to a central server via JSON/XML over HTTP, and then other devices pull updates from that.
Obviously it depends how much data and how many devices you want to sync across, and whether you have access to an appropriate server and/or budget to cover running such a server. iCloud will do that for "free" but all it really does is transfer files. A custom solution allows you to define your syncing model as you wish, but you have to develop and manage it and pay for it.

I've considered the possibility of transferring a database file through iCloud but I think that I would run into classic problems of timing - slow start for the user - and corrupted databases if the app is run on multiple devices simultaneously. (iPad/iPhone for example).
Sooo. I've had to use the transaction logs method. It really is difficult to implement, but once in place, seems ok.
I am using Apple's SharedCoreData sample as the base for this work. This link requires an Apple Developer Account.
I did find a much much better solution from Tim Roadley however this only works for IOS and I needed both IOS and MacOS.
rant> iCloud development really has to get easier and more stable! /rant

Related

Periodic iCloud backup of SQLite database

Let me get this out of the way right now: yes, it was almost certainly a mistake to not use Core Data. However, I was new to iOS development when I made these decisions, and I had no idea I'd be hamstrung like this. Moreover, the app is intended to also run on Android (eventually), so I avoided platform-specific APIs wherever possible.
I have an iOS app that stores data in a local SQLite database file. The data stored in the file is provided by the user, so it's important that it be kept safe. I had plans to "do this later", and later is now here. I am quickly coming to the realization that it won't be as straightforward as I had hoped...
I now understand that it won't be possible to seamlessly synchronize data across devices, and I'm willing to accept that limitation until I manage to migrate to Core Data. However, in the meantime I'd at least like the SQLite database to be backed up periodically so users can feel safe using the app on a single device. I was thinking I would do this:
periodically (e.g. once a week) copy the SQLite file from local storage into cloud storage, thus ensuring it is backed up
when the app starts, if the local store is missing or corrupted but the file exists in the cloud storage, ask the user if they would like to copy it over
The biggest problem with this approach is that the user could run the app on multiple devices and therefore the data stored in iCloud could be from any one of those devices, but only one. To combat that, I thought I could just use a per-device, unique name for the file in cloud storage. I would generate this using UIDevice.identifierForVendor.
So my startup logic would be:
Determine the unique name for the cloud file.
Is the local file missing or corrupted, and if so, does the cloud file exist?
2.1. Ask the user if they would like to restore from the cloud file. Make it really hard for them to say no because doing so will lose all their data.
2.2. If they say yes, copy the cloud file to the local file storage.
Open the local database file.
And running in the background I would occasionally copy the database file from local to cloud storage.
I would like to know whether this a sensible approach until I do Core Data integration. Also, are there any hidden "gotchas" that I'm perhaps missing?
UPDATE: as #TomHarrington pointed out in a comment, it turns out my database file is already sitting in /Documents, which is backed up to iTunes and any iCloud account. So my question morphs into this:
Should I simply ensure my database has a device-specific name so that it is not clobbered by the app running on another device connected to the same iCloud account?
I'm going to answer my question, since I ended up going down this path and finding a MASSIVE blocker. There is a bug in the UIDevice.identifierForVendor API that causes it to regenerate every time a new version of the app is installed! See here. This of course rules out using it as a device identifier. sigh
I think I'm SOL with that approach. Instead, I might generate a GUID on first execution and use that as my identifier. Problem is, I need to store that somewhere that isn't backed up to iCloud.
Ugh, I may just give up here and say my app can't be run on multiple devices until Core Data integration is done.
UPDATE: I ended up generating an identifier on first run and storing it in the keychain (as a local entry only so it isn't backed up to iCloud).

Ship iOS app with pre-populated Parse datastore

Given the recent addition of local datastore for iOS to Parse, it should be possible to rely exclusively on Parse to manage app's database, thus totally avoiding Core Data. Does this sound like a good idea? What would be the pros and cons of such an approach?
In particular, I am wondering whether it will be possible to pre-populate Parse local datastore with some data, and include this database as a part of the app when submitting to appstore.
UPDATE
From the comments that were posted, it seems that people misunderstood my intended use case. Sorry guys, I should have made my question more clear from the beginning. Let me clarify it now, anyway.
So, there is some amount of data in Parse database on the web, same for every user, e.g. a catalogue of books. It will be updated every now and then. What I want is to publish an app on App Store which is pre-populated with Parse data store, as it stands at the moment when the app is published. For that to happen, I'd like to pin all available data when building my app and ship that data store along with the app. The problem is that the pinned data will be stored on device's (or emulator's) file system, it won't be part of the project. That's why if I build the app and submit it to app store, the data won'd be included.
Any suggestions how to attach the local data store to the app?
The local data store is stored in the sandboxed part of the filesystem in iOS. When you package the store with the app, it'll live in the signed application folder, not in the location Parse expects it to be.
So, if you were looking to do this, you'd need to include your default local data store in the application on building/submission, and copy it into the location Parse expects it to be in (which is Library/Private Documents/Parse and the file is called ParseOfflineStore) when your application starts up. This must happen before you call enableLocalDatastore, or an empty one will be initialized.
It should be possible!
Read this in the docs. Parse has a highly resourceful and fully documented guide for their backend.
https://parse.com/docs/ios_guide#localdatastore
Per my comment above concerning didFinishLaunchingWithOptions; it has been a place for your to create objects on launch, I have been doing that for a long time. Especially with channels. However, by enabling the local data store you can access those objects you pinned or created with a simple query with no reachability per your concern. Either way they both are created on disk. Core Data has a lot more cons. Especially with NSFetchedResultsController and the flexibility it offers. It's all up to you what you want to do with your app. PFQueryTableViewController isn't bad but if your direction and vision for your app is to be exclusively Parse then why not. It's a great feature. However I didn't see anything in the docs about the local queries effecting your limit so I would suggest looking into that if you have a large audience performing numerous queries per second.
Take advantage of their docs. They do a great job at keeping us informed.

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;

Has Apple fixed the CoreData + iCloud sync issues? [closed]

There has been a lot of discussion lately about the issues with iCloud and Core Data and how Apple's APIs are currently broken in iOS 5 and possibly iOS 6.
Is it possible, given the current state of Apple's Core Data API, to reliably sync across multiple devices using iCloud?
If so, how would you do this? If not, please recommend an alternative approach.
This blog post will lead you to a chain of recent articles about the travails of developers attempting this approach.
From my own understanding and experience, I believe it is doable, but don't buy into the idea that you will get anything "for free". Depending on your data model, you may be better off syncing your whole persistent store as a document rather than using the documented core data / iCloud approach.
You may have better luck if you're already comfortable with Core Data. Just be sure you think through how to handle several important cases.
One is what to do if the user signs out of their iCloud account. When this happens, the local ubiquitous persistent store is deleted. If it makes sense for the user to still have access to their data, you'll need to manage a copy in local storage, and then manage resynchronizing when they sign back in.
Another is that changes can apparently be quite slow to propagate by default, so you may want to consider an alternative mechanism, such as the key value store, to quickly propagate sufficient information to avoid a bad user experience.
Conflict management is perhaps the most challenging (depending on your model). While the framework provides a mechanism to inform you of conflicts, you are on your own for providing a mechanism to resolve them, and there are reports that the conflict notifications may not be reliable (see linked articles), which seems strongly linked to the lag in updating.
In short, if you go into this understanding that the actual support is pretty bare bones and that you'll need to code very defensively, you may have a chance. There aren't any good recipes out there, so if you do make it work, please come back and tell us what works!
It depends on what you want to do. There are two types of Core Data-iCloud integration, as described here: http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html
There are broadly speaking two types of Core Data-based application that integrate with iCloud:
Library-style applications, where the application usually has a single persistent store, and data from the store is used throughout the application.
Examples of this style of application are Music and Photos.
Document-based applications, where different documents may be opened at different times during the lifetime of the application.
Examples of this style of application are Keynote and Numbers.
If you're using the library-type, this article is the first of a series that goes into a lot of the problems that will come up: http://mentalfaculty.tumblr.com/post/23163747823/under-the-sheets-with-icloud-and-core-data-the-basics.
You can also check out sessions 218 (for document-based) or 227 (for library-style) of this year's wwdc.
As of iOS 7, the best solution is probably the Ensembles framework: https://github.com/drewmccormack/ensembles
Additionally, there is a promising project which will essentially allow you to do the same thing using a different cloud service.
Here is a link to the repository: https://github.com/nothirst/TICoreDataSync
Project description:
TICoreDataSync is a collection of classes to enable synchronization via the Cloud (including Dropbox) of Core Data-based applications (including document-based apps) between any number of clients running under Mac OS X or iOS. It's designed to be easy to extend if you need to synchronize via an option that isn't already supported.
Reasons for why iCloud is not currently reliable:
"Sometimes, iCloud simply fails to move data from one computer to another."
"Corrupted baselines are [a] common obstacle.... There is no recovery from a corrupted baseline, short of digging in to the innards of your local iCloud storage and scraping everything out, and there is no visible indication that corruption has occurred — syncing simply stops."
"Sometimes, when initializing the iCloud application subsystem, it will simply return an opaque internal error. When it fails, there’s no option to recover — all you can do is try again (and again…) until it finally works."
"[W]hen you turn off the “Documents & Data” syncing option in the iCloud system preferences, the iCloud system deletes all of your locally stored iCloud data[.]"
When you sign out of iCloud, the system moves your iCloud data to a location outside of your application’s sandbox container, and the application can no longer use it.
"In some circumstances (and we haven’t been able to figure out which, yet), iCloud actually changes the object class of an item when synchronizing it. Loosely described, the object class determines the type of the object in the database[.]"
"In some cases (again, not all the time), iCloud may do one of the following:
Owner relationships in an item’s data will point to the wrong owner;
Owner items get lost in synchronization and never appear on computers other than the one on which they were created. (This leads to the item never appearing in the UI on any other machine.) When this happens, bogus relationships get created between blob items and an arbitrary unrelated owner."
"Sometimes (without any apparent consistency or repeatability), the associated data for an object (for example, the PDF data for a PDF item, or the web archive data for a Web Archive item) would simply fail to show up on the destination machine. Sometimes it would arrive later (much later — minutes or hours)."
Quoted and paraphrased from these sources:
http://www.imore.com/debug-12-icloud-core-data-sync
http://rms2.tumblr.com/post/46505165521/the-gathering-storm-our-travails-with-icloud-sync
Note: I have seen one article where the author mentions getting it to work for iOS 6+, but they don't provide any examples: http://zaal.tumblr.com/post/46718877130/why-you-want-to-use-core-data-icloud-sync-if-only-it
As a reference, here are Apple's docs on iCloud + Core Data:
http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/
http://developer.apple.com/library/ios/#documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesignForCoreDataIniCloud.html
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/CoreDataVersioning/vmCloud/vmCloud.html
And here is an example app:
http://developer.apple.com/library/ios/#DOCUMENTATION/General/Conceptual/iCloud101/Introduction/Introduction.html
The Apple developer tutorial on using the iCloud API to manipulate documents might be a good place to start.
Your Third iOS App introduces you to the iCloud document storage APIs. You use these APIs to store and manipulate files in a user’s iCloud storage.

Syncing Core Data across multiple devices using iCloud

There has been a lot of discussion lately about the issues with iCloud and Core Data and how Apple's APIs are currently broken in iOS 5 and possibly iOS 6.
Is it possible, given the current state of Apple's Core Data API, to reliably sync across multiple devices using iCloud?
If so, how would you do this? If not, please recommend an alternative approach.
This blog post will lead you to a chain of recent articles about the travails of developers attempting this approach.
From my own understanding and experience, I believe it is doable, but don't buy into the idea that you will get anything "for free". Depending on your data model, you may be better off syncing your whole persistent store as a document rather than using the documented core data / iCloud approach.
You may have better luck if you're already comfortable with Core Data. Just be sure you think through how to handle several important cases.
One is what to do if the user signs out of their iCloud account. When this happens, the local ubiquitous persistent store is deleted. If it makes sense for the user to still have access to their data, you'll need to manage a copy in local storage, and then manage resynchronizing when they sign back in.
Another is that changes can apparently be quite slow to propagate by default, so you may want to consider an alternative mechanism, such as the key value store, to quickly propagate sufficient information to avoid a bad user experience.
Conflict management is perhaps the most challenging (depending on your model). While the framework provides a mechanism to inform you of conflicts, you are on your own for providing a mechanism to resolve them, and there are reports that the conflict notifications may not be reliable (see linked articles), which seems strongly linked to the lag in updating.
In short, if you go into this understanding that the actual support is pretty bare bones and that you'll need to code very defensively, you may have a chance. There aren't any good recipes out there, so if you do make it work, please come back and tell us what works!
It depends on what you want to do. There are two types of Core Data-iCloud integration, as described here: http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html
There are broadly speaking two types of Core Data-based application that integrate with iCloud:
Library-style applications, where the application usually has a single persistent store, and data from the store is used throughout the application.
Examples of this style of application are Music and Photos.
Document-based applications, where different documents may be opened at different times during the lifetime of the application.
Examples of this style of application are Keynote and Numbers.
If you're using the library-type, this article is the first of a series that goes into a lot of the problems that will come up: http://mentalfaculty.tumblr.com/post/23163747823/under-the-sheets-with-icloud-and-core-data-the-basics.
You can also check out sessions 218 (for document-based) or 227 (for library-style) of this year's wwdc.
As of iOS 7, the best solution is probably the Ensembles framework: https://github.com/drewmccormack/ensembles
Additionally, there is a promising project which will essentially allow you to do the same thing using a different cloud service.
Here is a link to the repository: https://github.com/nothirst/TICoreDataSync
Project description:
TICoreDataSync is a collection of classes to enable synchronization via the Cloud (including Dropbox) of Core Data-based applications (including document-based apps) between any number of clients running under Mac OS X or iOS. It's designed to be easy to extend if you need to synchronize via an option that isn't already supported.
Reasons for why iCloud is not currently reliable:
"Sometimes, iCloud simply fails to move data from one computer to another."
"Corrupted baselines are [a] common obstacle.... There is no recovery from a corrupted baseline, short of digging in to the innards of your local iCloud storage and scraping everything out, and there is no visible indication that corruption has occurred — syncing simply stops."
"Sometimes, when initializing the iCloud application subsystem, it will simply return an opaque internal error. When it fails, there’s no option to recover — all you can do is try again (and again…) until it finally works."
"[W]hen you turn off the “Documents & Data” syncing option in the iCloud system preferences, the iCloud system deletes all of your locally stored iCloud data[.]"
When you sign out of iCloud, the system moves your iCloud data to a location outside of your application’s sandbox container, and the application can no longer use it.
"In some circumstances (and we haven’t been able to figure out which, yet), iCloud actually changes the object class of an item when synchronizing it. Loosely described, the object class determines the type of the object in the database[.]"
"In some cases (again, not all the time), iCloud may do one of the following:
Owner relationships in an item’s data will point to the wrong owner;
Owner items get lost in synchronization and never appear on computers other than the one on which they were created. (This leads to the item never appearing in the UI on any other machine.) When this happens, bogus relationships get created between blob items and an arbitrary unrelated owner."
"Sometimes (without any apparent consistency or repeatability), the associated data for an object (for example, the PDF data for a PDF item, or the web archive data for a Web Archive item) would simply fail to show up on the destination machine. Sometimes it would arrive later (much later — minutes or hours)."
Quoted and paraphrased from these sources:
http://www.imore.com/debug-12-icloud-core-data-sync
http://rms2.tumblr.com/post/46505165521/the-gathering-storm-our-travails-with-icloud-sync
Note: I have seen one article where the author mentions getting it to work for iOS 6+, but they don't provide any examples: http://zaal.tumblr.com/post/46718877130/why-you-want-to-use-core-data-icloud-sync-if-only-it
As a reference, here are Apple's docs on iCloud + Core Data:
http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/
http://developer.apple.com/library/ios/#documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesignForCoreDataIniCloud.html
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/CoreDataVersioning/vmCloud/vmCloud.html
And here is an example app:
http://developer.apple.com/library/ios/#DOCUMENTATION/General/Conceptual/iCloud101/Introduction/Introduction.html
The Apple developer tutorial on using the iCloud API to manipulate documents might be a good place to start.
Your Third iOS App introduces you to the iCloud document storage APIs. You use these APIs to store and manipulate files in a user’s iCloud storage.

Resources