I am devloping an app using CloudKit and CoreData. I currently have the app setup so that when the user changes, I use a different locaton for the persistant store. If the user loggs back in, I can switch back to that persistant store and pick up right where they left off and not have to redownload data up to that point as well as not loose any changes that might not have been synced when the account changed.
My question is I am now second guessing if this is the right approach (from a data security standpoint) and want to know if there is Apple guidance in this scenario or what other developers might think. Should I just purge the data when the accont changes (and if there is data loss, not worry about it).
Is there anything I can do about unsynced data when an account changes?
I appreciate any input.
This is a curious use case. I think the answer depends on a few things.
If people are sharing devices and signing in/out of the app, and the app data they are saving is sensitive (PII, financial data, etc.), then I would purge the data and download the new user’s stuff from CloudKit.
If people aren’t sharing devices, and the amount of data you need to pull down is pretty big (several hundred MB or more), then I would keep the local cache in Core Data.
I hope that helps.
Related
If my objective is to create a cloud backup that a user can restore if they accidentally delete my app or a bug causes data loss (no need to share data across devices), what's the simplest option to backup user CoreData daily?
(Note: The occasional full iPhone backup isn't sufficient)
When I read about CloudKit I see things like:
You might wonder why you should choose CloudKit over Core Data, other commercial BaaS (Back end as a Service) offerings, or even rolling your own server...
But that's a problem because I would like to continue using CoreData! I do not want the user to depend on the cloud for data. I don't want syncing problems, online/offline issues, etc.
I would also like to avoid having the user login or create an account. According to Ray Wenderlich:
Since CloudKit uses the iCloud credentials entered when the device is set up (or entered after set up via the Settings app), there’s no need to build complicated login screens.
I see some apps use Facebook login but I'd even like to avoid that! So can I use CloudKit to avoid login screens but also still use CoreData?
Although code answers are great, just a general answer on what API/design-pattern is used for this type of functionality would suffice.
Yes you can use CloudKit at the same time as CoreData to do this and it does avoid the login and cost overheads of many other cloud alternatives.
However as the two data models are not related, if your data is complex there will be a significant amount of code necessary to covert between them whenever the user selects to backup or restore.
Are you sure relying on the normal iOS backup functionality is not sufficient?
I am building a fitness app with HealthKit integration. Ultimately I would like to use CloudKit as well to a) allow data redundancy, but mainly b) to provide a few social features which require data to be in iCloud at least temporarily.
I would like to be able to rely solely on HealthKit for data within the app, but feel that an alternate data model is necessary to persist data incase HealthKit permissions are revoked or not given in the first place. I have chosen to stick with Core Data for this.
My question is how do I go about keeping my Core Data store and my HealthKit store in sync. I have searched for an example on GitHub and for related questions on here, but cannot find any useful examples.
Ultimately I will be then syncing the data in Core Data with CloudKit, but is the Core Data intermediary really necessary?
With regards to App Store Review Guidelines 27.3
Apps using the HealthKit framework that store users’ health
information in iCloud will be rejected
I take this to mean that any Health data which was not created by your app cannot be stored in iCloud. There are many apps which store Health data on a third party server (i.e. RunKeeper). Also, without HealthKit permissions I would be allowed to store health data created by my app in iCloud. If you take third party data from HealthKit and try to put that in iCloud, then you'll be rejected.
There's a couple questions in here, so I'll try to answer them in order.
...how do I go about keeping my Core Data store and my HealthKit store in sync?
So there are two application modes you need to worry about for getting data updates: foreground and background.
When in the foreground, you can utilize HKObserverQuery which provides a decent amount of flexibility in getting the data you need. The usual caveats apply when passing data across thread boundaries (as observer queries run on background queues). Pertinent docs: HKObserverQuery Docs
In the background you have to register for background wakeup using enableBackgroundDeliveryForType(_:frequency:withCompletion:). This will wake your application at (or close to) the specified frequency, at which point you'll need to jump through whatever necessary hoops to load your Core Data stack and do your updates. Pertinent Docs: HKHealthStore Background Handling Docs
...is the Core Data intermediary really necessary?
No, and in fact using Core Data may be complete overkill for your uses. I generally recommend against implementing Core Data at the outset of an application. There are performance concerns, background wake concerns, schema migration concerns when you change schemas, and iCloud <> Core Data synchronization issues (most of which have been resolved as of iOS 9 but still crop up occasionally).
On top of all that, Apple's "template" for including Core Data in a new project generally doesn't follow best practice guidelines for integrating Core Data. Do some Google searches and take a look at Marcus Zarra's books on the subject.
That all said, not using Core Data means having to write a bunch more code to enable iCloud to synchronize with your data store of choice, so it's tough to offer a suggestion as to the "correct" route to take.
I take this to mean that any Health data which was not created by your app cannot be stored in iCloud.
Incorrect. Take the line at face value. If Apple sees you've requested access to HealthKit and have provisioned iCloud access, you're probably going to get scrutinized and most likely rejected. They are very touchy about user privacy in this regard, and correctly so in my opinion.
Your note about Runkeeper, while accurate, is also flawed as Runkeeper (last I checked) does not use iCloud and so wouldn't be subject to this, so the analogy is flawed. Also, as per Apple's other guidelines, whatever other apps do or don't do has no bearing on your application's review status.
In short, I would steer clear of storing users' health data in iCloud. Use another provider or your own server.
I know Apple will reject apps that backup data that can be reloaded from the app bundle. However, in the app I'm working on, we'll be providing some basic data that users will want to keep in the Core Data store along with the new data they have created and entered. So, users will use the "library" data we provide by integrating it with their own data as they work with the app.
My concern is whether Apple will require me to segregate these data in some way such that data from the bundle is not backed up to iCloud? Once the data are co-mingled, disaggregation would be complex and burden the app excessively.
So, my question is whether I have to concern myself with the co-mingling of user data with that data which is supplied in the app bundle?
TIA for any input on this as I've not found anything about it in the docs.
I had a similar situation in my app. It has sample data, but the sample data is designed to be edited by the user, and has metadata attached to it that is user specific. In other words, the data is mutable, and belonged with the rest of the user's personally created data.
At one point, the app was rejected. I first appealed to the reviewer, explaining that the data was mutable, and personal to the user — it would not be possible to reproduce the data purely from the app bundle. The reviewer would not budge, so I appealed, and they sided with the reviewer. Game over.
You may be able to get away with a small amount of data loaded from your bundle (<1MB). If you plan to include images or anything that will push up that amount, they will likely reject you.
I also heard from another developer rejected for including data downloaded from a web service in the user's store. Apple claimed that the data could be re-downloaded, which may be partially true, but ignores the fact that the web service was operated by a third party, and the downloaded data could be unshared at any time. It also ignores that the user can edit the downloaded data, and that the client app is attaching user-specific metadata that cannot be downloaded.
That developer was rejected as well. He eventually got the app through review by introducing a complex set of procedures to import the data: the user had to first download the data to a temporary holding area that was not backed up. They then had to explicitly import the data, and dismiss a wordy dialog warning that the imported data would contribute to their iCloud backup quota. Horrible. But that is apparently what Apple wants.
The rule is pretty mindless, in my view, and Apple seem to be completely oblivious to the implications in terms of user interface changes and drastic refactoring of an app's model. I can't imagine any engineers were involved in forming the rule, because they surely would have realized how much unnecessary complexity it would impose on developers. We can only hope they see reason at some point.
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;
As someone that experienced the pain of iCloud while trying to prototype iCloud enabling one of our CoreData apps, Simperium looks very promising, but I'm interested in seeing how it handles some of the sharp edges.
One issue I came across was how to gracefully handle bootstrapping data when the application starts up. The first time a user launches our app, we will load some default data into our CoreData database. If a user launches the app first on the iPhone and then later on the iPad, they will end up getting the bootstrap data duplicated on both devices because of syncing. With iCloud, the solution was to hook into the iCloud merge process.
How would I handle this with Simperium?
There are at least a couple ways to do this.
You can hardcode the simperiumKey for each seeded object. For example, in a notes app, if every new user gets a welcome note, you can locally create that note with the simperiumKey of welcomeNote. This will ensure that only one welcome note will ever exist in that user's account (on any device). With this approach, there can be some redundant data transfer, so it's best if there's not a large amount of seeded data. On the other hand, this approach is good if you want data to be immediately available to new users even if they're offline when they first launch your app.
With Simperium, you also have the option to use a server process. You can seed new user accounts with data by using a Python or Ruby listener that runs some code when accounts are created. This is a good approach if there's a large amount of data, but has the disadvantage that users need to be online before the seeded data will transfer (and of course the transfer itself will take some time).
There are subtleties with these approaches. With the first approach, using the welcomeNote example, if your user deletes the welcomeNote and subsequently reinstalls your app in the future, the welcomeNote will get resurrected (but never duplicated) because it's being created locally. This is often acceptable. With the second approach, the welcomeNote would be seeded once and only once, so it will never get resurrected even if your app is reinstalled.