My app was rejected recently due to the fact that it installs a database within a directory which will be backed up to iCloud. As the database comes with a lot of prepopulated data and the app stores user generated data into the same file.
So mixing up user-generated-content with prepopulated data is not was Apple wants us to do.
So far so good.
Separate my database into two and mark the store file with the prepopulated data with NSURLIsExcludedFromBackupKey = YES.
But what happens if the user wants to modify the data in that store, because he found a failure and wish to modify it.
Or myself make an online update available which modifies values with that store.
How do I cope with that.
Do I have to delete the store file, create a new one (now with NSURLIsExcludedFromBackupKey = NO) or store the database under /tmp or /Library/caches right from the beginning and move it into /Application Support (which is backed up automatically) but with the threat that my database is being removed by the system for some reason what is the case for /Library/caches?
It is a bit annoying that Apple will not allow you to backup prepopulated data if your app is of the kind where you could actually change the prepopulated data in the app. If the prepopulated database is big, I can however understand that they don't want your app to waste the user´s iCloud space with information that is already in the AppStore.
Woody has a good idea on the approach, but I'm not sure Apple would look away from the fact that you are actually wasting just as much space if you copy the prepopulated data to the user-backed-up DB on app startup.
What about something like this:
A: DB with pre-populated data, not backed up
B: DB with user added
data, backed up
When user make changes to object in A, crete a new row in B that "overrides" the row in A, for example by using the same ID or by having a column in the DB that tells the app which object in A should be replaced by the new row in B.
Whenever you need to update your app, you will replace DB A with new content and that's it. This could lead to conflicts with the data that the user has changed. You will have to decide whether the user data is more important than the updated data, and how to handle these conflicts (for example by trying to keep them both).
If you need to change the structure of DB B in an update, for example if you need to add a column, you will have to include an update routine in your app that detects that the user is having an old DB version and write code to migrate the user data to the new database on first startup after the update.
When you startup, if the user database is unpopulated you could copy the data across from the pre-populated datatabse, and maybe give the user an option to reset defaults which does the same again?
Related
Our app will ship with a pre-populated database which continually downloads updates from our production server's database to stay up to date. When we make updates to our app's codebase and push those updates out to the AppStore we'll also include an updated db.sqlite file.
My questions is: when we do this I assume it will overwrite the users "old" database file that already exists?
I assume (and hope) the answer is yes, but just wanted to double check - I couldn't find any answers in Apple's documentation, but might have simply overlooked, or have been looking for the wrong terminology.
When you say "which continually downloads updates from our production server's database" I assume that means the app will download the data and update the database? In which case the database must exist in the Documents folder and it won't get into the Documents folder until you copy it there.
Normally this is done by the app if the database file doesn't already exist, however if you want to overwrite the current database you'll need a mechanism to discover if the one in the Documents folder is out-of-date, compared to the one in the app bundle. This probably means having a "metadata" table (with two columns name/value) containing a version number or some such. You read both databases and decide whether or not to copy.
In my IOS app, I store two types of data in CoreData. One type is user generated, the other type is a pregenerated database. Currently this is all in the same store, which goes into the documents folder. But this is not a good approach, so I will split into 2 separate stores.
There are two problems:
The pregenerated database should not be backed up by iCloud.
When an update of the app is provided, the user data should be kept, but the pregenerated database should be overwritten. (The pregenerated database will have updated content, even if the data model is unchanged.)
For problem 1, I can either put the pregenerated database store in the cache directory, or keep it in the documents directory flagged for skipping backup. As the cache directory can be emptied any time, using the documents folder without backup seems better. However, does that solve problem 2? That is, will the pregenerated database be overwritten after an update?
If not, are there any other solutions?
You can keep pregenerated rated database in document folder and flag it for skipping backup in iCloud. However you have to write some script to overwrite the data Or on update you can delete your pregenerated store completely and create it again with updated data.
I am creating a core data app with preloaded data using an SQL file. I am able to create the preloaded data, insert that SQL file into the project, and there is no problem. When users open the app for the first time the pre-populated store is copied over to the default store.
However, I am thinking ahead that in future versions I will want continue to add to this database. I will want users to be able download the current version with the latest DB without erasing user-generated data or user-edits to data in the preloaded DB.
This is not a migration issue because the model has not changed. Basically, when a new version of the app is opened for the first time I want it to check for the presence of new objects in the pre-populated store and add them to the user store. Any suggestions are welcome.
Make your preloaded data include, for each object, the version where that object was first added to the preload file. Then add new data by
Look up the previous app version in user defaults. Compare it to the current version. If they're the same, stop, otherwise continue to the next step. (If there is no previous version number, continue to the next step).
Fetch all objects that were added more recently than the saved version number from step 1.
Add those objects to the user's persistent store.
Update the app version in user defaults so it'll be current next time.
You can do this check every time the app launches. You'll want to save a numeric version number in user defaults that will always increase in future versions of the app.
The simple way to record the version info in the preload file is just to add an extra field on each entity type. A more elegant way would be to create a new entity called something like VersionWhereAdded that includes a version number and a to-many relationship to objects added in that version.
First, Thank you for any help provided.
I have an iOS leveraging CoreData to retain various presentations, this data comes from a sqlite file and there is no server connection.
I will have to be able to provide App updates (via appstore), this update may add more data to the database.
The tricky part is that it can not simply overwrite the current database, there are a few user tables that I will not like touched.
Please provide any information I should consider when accomplishing this or any links are greatly appreciated.
Thank you.
Given your app has no server connection, you will have to rely on shipping data within the updated application itself. I would recommend using a plist file or define your own xml or json structure. You can then read this data to create/update core data nsmanagedobjects.
It looks like someone in the past was using plist->coredata on SO
Would you have relationships between user created data and shipped data?
If not, you might go the route of connecting two stored to the persistent store coordinator. The shipped store would be read-only. The store with user created data would be read-write. You can use this approach, too, if you have relationships between shipped and user-created objects, but it's a lot more complicated, since CoreData doesn't manage cross-store relationships for you, and you'll need to write your own logic (doable, but not straight forward).
If you need to have relationships between shipped and user-created objects, you can still ship a CoreData store. When the app launches for the first time (no user-created objects), you copy the store to the Documents folder and user this store to create your CoreData stack. User created objects will be added to this store. Once you have new 'shipped' objects (i.e. a new store in the app-bundle), you'll have to manually migrate that stores data into the store that the user has changed. You'll have to be able to find
(1) objects that need to be deleted
(2) objects that need to be updated (changed)
(3) objects that need to be added
If you mark your shipped objects with a special flag such that you can tell if it's a user created object or a shipped one, that would be doable. You also have to have some sort of ID to be able to tell which objects in the new store correspond to which ones in the existing (old) store.
You do not need to go the route of using plists. In fact, I'd recommend against it. You can easily open two stores at the same time. Either to use both stored, or just to migrate objects from one store to the other store.
I have an existing iOS app that uses core data for app data and user data. My problem is that updating app data is a nightmare (my first app, so I didn't do it ideally the first time). I would like to split the app data and user data into 2 separate sqlite dbs (or stores, correct me if my terminology is wrong).
Any tips would be appreciated.
Having two sqlite files is a good idea. The pain is splitting them now.
Create a new store that only exists in your app bundle. Make sure the data is unchanged from when you first released the app.
You are going to need to walk the "user" store and find all of the data that is identical to what exists in the "reference" store and delete it. If the user has changed that data then I would leave it and let the user sort out duplicates.
Once that is complete your app can resume normal function and load up both stores. I would set a flag somewhere so that you know this has been done and you don't run the check on every launch. The "user" store's metadata is a good place.
Note, this will need to be done before the user can "use" your app. This probably means changing your launch routines so that if a migration and filter is needed you tell the user what is going on.
I don't think having multiple persistent stores is the right solution. You can simply have separate entities within a single persistent store. Core Data will handle it properly.