I am using Parse.com as backend for an iOS application. When I get all the remote objects from a relation and pin them, I can access them locally later.
However, I want to use the "updatedAt" property on a Parse object to only fetch updated objects remotely and update the local datastore.
How it is done:
query.whereKey("updatedAt", greaterThan: lastDate)
let results = try query.findObjects()
try PFObject.pinAll(results)
Let's say I have 10 objects locally, and the remote query only returns 1 object. After pinning the one returned object, the other 10 local objects are removed. The result is that only the one new remote object is saved locally.
Is it possible to use the "updatedAt" key to only fetch new objects and still have all objects in the local database? How is this done?
Thanks in advance!
Related
I need to synch up Core Data on phone with MYSQL database on server. I have gotten as far as capturing JSON feed in iOS in array. My question is, what is the best way to compare and sync up the items?
I know there are many SO posts and tutorials on syncing however the comparison is often brushed over. Once you have the JSON feed in an array and also have a managed object context, how do you compare one entry against the other?
My strategy is to give each locally saved object (record) a local id. Then when syncing occurs if the local object (record) has no server id, add it to the server table. However, I'm not sure how that works with the managed object context.
Do you iterate through the local records and compare them against server records?
Or do you iterate through the JSON array against the local managed object context?
for (NSDictionary *item in names) {
id serverid = [item objectForKey:#"serverid"];
// .... check against something in managed objectcontext
if (!items.serverid ) {
///insert on server and also provide obtained serverid to managedobjectcontext
}
}
Would appreciate any insights or suggestions.
Tim Isted addressed a similar issue in his exceptional talk at WWDC 2013.
You should watch the whole thing, because it's pretty awesome. However, the relevant part for this topic starts about 12:43 into the presentation.
My goal is to see which PFObjects have been saved locally with the Parse's Local Datastore. I can save objects with myObject.pinInBackground() and I can retrieve them with getObjectInBackgroundWithId given an objectId.
So do I have to store the objectIds to see which objects have been stored locally? Isn't there a better way to list all the objects saved?
You can query local datastore objects by calling the query.fromLocalDatastore() method before executing the query.
https://parse.com/docs/ios/guide#queries-querying-the-local-datastore
I'm trying to sync my data from a web service in a simple way. I download my data using AFNetworking, and using a unique identifier on each object, I want to either insert, delete or update that data.
The problem is that with Core Data you have to actually insert objects in the NSObjectManagedContext to instantiate NSManagedObjects. Like this:
MyModel *model = (MyModel *)[NSEntityDescription insertNewObjectForEntityForName:#"MyModel" inManagedObjectContext:moc];
model.value = [jsonDict objectForKey:#"value"];
So when I get the data from the web service, I insert them right away in Core Data. So there's no real syncing going on: I just delete everything beforehand and then insert what's being returned from my web service.
I guess there's a better way of doing this, but I don't know how. Any help?
You are running into the classic insert/update/delete paradigm.
The answer is, it depends. If you get a chunk of json data then you can use KVC to extract the unique ids from that chunk and do a fetch against your context to find out what exists already. From there it is a simple loop over the chunk of data, inserting and updating as appropriate.
If you do not get the data in a nice chunk like that then you will probably need to do a fetch for each record to determine if it is an insert or update. That is far more expensive and should be avoided. Batch fetching before hand is recommended.
Deleting is just about as expensive as fetching/updating since you need to fetch the objects to delete them anyway so you might as well handle updating properly instead.
Update
Yes there is an efficient way of building the dictionary out of the Core Data objects. Once you get your array of existing objects back from Core Data, you can turn it into a dictionary with:
NSArray *array = ...; //Results from Core Data fetch
NSDictionary *objectMap = [NSDictionary dictionaryWithObjects:array forKeys:[array valueForKey:#"identifier"]];
This assumes that you have an attribute called identifier in your Core Data entity. Change the name as appropriate.
With that one line of code you now have all of your existing objects in a NSDictionary that you can then look up against as you walk the JSON.
The easiest thing to do is to restore the Json to a entity that maps properly to it. Once you've mapped it, determine if a object matching the entities ID exists already, if so then fetch the entity and merge changes. If not, create a new entity in Core Data and restore the Json to it.
I'm building a app were I do client side syncing with Evernote. They keep a syncUpdate number on all of their objects and at the server level. So when I start my sync I check if my clients syncUpdate count is less than the servers. If so, I know I am out of sync. If my updateCount is at 400 and the server is at 410, I tell the server to provide me with all objects between updateCount 400 and 410. Then I check if I already have the objects or not and perform my update/create.
Every time a object is modified on the server, that objects updateCount is increments along with the servers.
The server also keeps a time stamp of the last update, which I can check against also.
I have set up AFIncrementalStore to grab objects from a JSON service over the network and set its persistentStore to be an SQLite database. This all works fine.
Now what I want to do is add objects to that SQLite database out-of-band (from something other than the web service the AFIncrementalStore is pointing to), and have those additions reflected in the fetched results controllers created from the original managed object context.
I've created a managed object context with the original MOC as its parent and I can add objects to that and they're seen by the fetched results controller. But they're not saved to the AFIncrementalStore's SQLite db. Interestingly, AFIncrementalStore is seeing these objects as it was trying to save them back to the JSON service and complaining the correct endpoint didn't exist (I fixed this by overriding requestForInsertedObject:insertedObject to return nil.)
Anyone know how I achieve this?
If you don't want to POST objects to the server you have to override methods
requestForInsertedObject:insertedObject
requestForUpdatedObject:updatedObject
requestForDeletedObject:deletedObject
Then when you call context's save: method your objects must be saved in the database. I'm using similar logic when I'm doing CRUD operations offline and it is working fine.
It sounds like you have already found most of the answer. AFIncrementalStore checks for a nil response from requestForInsertedObject: in your AFRESTClient subclass. If that method returns nil, AFIS creates a permanent ID for the object, stores the object in the backing store and doesn't try to send it to the server again. This is all in the first section of executeSaveChangesRequest:withContext:error:.
Are you always calling save: on the parent ManagedObjectContext? If not, that would be the other reason it's not storing the object in SQLite. But then it shouldn't be trying to POST the object to the server either.
I don't have access to an OData provider just a simple REST api with json and I need to store the data locally (on the mobile device websql) in different tables reflecting the backend model. Following the Edmunds example I have got the entity and the relationships working from the REST api. How can I make it work the same way from the data stored locally. I would like to fetch the data from the local DB and recreate my entities, any advice would be appreciated thanks.
After you have queried the data thru the REST api, just export the EntityManager to local storage. Something like this
var changesExport = myEntityManager.exportEntities();
ok(window.localStorage, "this browser supports local storage");
var stashName = "arbitrary name for storage area"";
window.localStorage.setItem(stashName, changesExport);
This data can later be reimported into any existing EntityManager, and then queried locally by simply reimporting the data.
importedData = window.localStorage.getItem(stashName);
anotherEntityManager.importEntities(importedData);