For public CloudKit databases, does anyone know if the data tokens used for previousServerChangeToken when initializing a CKFetchNotificationChangesOperation are:
1) User specific: a data token can only be used by one Apple ID
or
2) Database specific: the same token can be used by different Apple IDs to specify the same state in the CKNotifications database
I'm wondering because I have some preloaded local data that may need to be updated on first launch, and I'm not sure if I should use CKFetchNotificationChangesOperation (with an initial token saved in the main bundle) or just fetch all the records in the public database and check against the local data.
Notification change tokens are a per-user value and they can not be used to assume anything about the state of records in the public database.
It sounds like you're trying to use the public database to store files that update the default files in your application's bundle.
One way to do this would be to store a version property on all of your records of this type. When an app checks to see if it needs any updates it can run a CKQuery for all records with a higher version than what is saved locally. Your app can then download all of those records and use them instead of the data stored in its bundle.
What you need is CKFetchRecordChangesOperation, but this only works for non-default zones in the user private cloudkit database.
This operation will give you all the changes over the record zone, records deleted and updates (with only the changed attributes).
Related
I have a problem.
I have iOS client app that has to allow multiple users to log in and store their data locally.
Data is synchronized with RESTful service, and the latest snapshot along with user's changes should be stored locally for all users.
Previously this app was implemented with SQLlite as data storage engine.
Now I would like to migrate to CoreData.
What do I have:
server returns me entities for current user. User ID is not sent, as the user authorizes and gets their session;
I know who is logged in an should store all data for this particular user. In order to do that I need to say CoreData to store the object for the user with ID=12345.
The problem is:
I have to tell CoreData to store the particular object associated with particular user's ID.
I need a way to somehow alter the object mapped with RESTkit - setting proper ID field for it.
This task was straight and simple with SQLlite but looks problematic with CoreData.
I am still thinking that I don't know something about CoreData asking you to help me with clarifications or useful links.
There isn't a good way to do it.
Hacky, you could add the id as a parameter in the request so you can map it back again (requires RestKit dev branch at time of writing).
Non-hacky is to update and re-save the objects returned in the mapping result.
Alternatively you could use one operation to download the JSON, then mutate it, then run another operation to map it.
I have just gone through the cloud kit doc as in this link cloudkit but not getting clear about to modify database records for public database. As per this description in cloudkit framework.
Using Public Database, as specified in image by default data are world readable, owner writable. That means only owner can modify his data other user can only read?
Suppose, using public database, user A can access records of user B, But can user A modify records of user B in same application.
Please suggest. Thanks.
In the CloudKit dashboard you are able to change the access rights. So it would be possible to make records writable by others. You do have to be careful with this. When your app runs on a jailbroken device, then it's possible to execute methods with arbitrary arguments. This is a high security risk.
if you set your security setting within the record types on the dashboard, you can have all your public records set to be writeable by anyone without messing with the security roles.
Set Authenticated to Write
Set Creator to create.
p.s. you cannot 'world' read from the simulator, you need to be logged in on that. On the device though, you don't need to be logged in to read the public database :-) which is nice.
We've an MVC web app which has a Claim management wizard (similar to a typical Order entry stuff). Claim can have multiple Items and each Item can have multiple files related to it.
Claim --> Items --> Files
While adding a new Claim - we want to allow the user to add one or more items to it and also allow file upload for those items. But we want to keep everything in memory until the Claim is actually saved - so that if the user doesn't complete the Claim entry or discards it, no database interaction is done.
We're able to handle data level in-memory management via session. We serialize the Claim object (which also includes a Claim.Items property) in session. But how to manage files?
We store files in < ClaimID >\< ItemID > folder but while creating a new
claim in memory we don't have any IDs until the record is being
saved in the database (both are auto-increment int).
For now, we've to restrict the user from uploading files until a Claim is saved.
Why not interact with the database? It sounds like you're intending to persist data between multiple requests to the application, and databases are good for that.
You don't have to persist it in the same tables or even in the same database instance as the more long-term persisted data. Maybe create tables or a database for "transient" data, or data that isn't intended to persist long-term until it reaches a certain state. Or perhaps store it in the same tables as the long-term data but otherwise track state to mark it as "incomplete" or in some other way transient.
You could have an off-line process which cleans up the old data from time to time. If deletes are costly on the long-term data tables then that would be a good reason to move the transient data to their own tables, optimized for lots of writes/deletes vs. lots of reads over time.
Alternatively, you could use a temporary ID for the in-memory objects to associate them with the files on the disk prior to be persisted to the database. Perhaps even separate the file-associating ID from the record's primary ID. (I'm assuming that you're using an auto-incrementing integer for the primary key and that's the ID you need to get from the database.)
For example, you could have another identifier on the record which is a Guid (uniqueidentifier in SQL) for the purpose of linking the record to a file on the disk. That way you can create the identifier in-memory and persist it to the database without needing to initially interact with the database to generate the ID. This also has the added benefit of being able to re-associate with different files or otherwise change that identifier as needed without messing with keys.
A Guid shouldn't be used as a primary key in many cases, so you probably don't want to go that route. But having a separate ID could do the trick.
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?
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.