core data undo specific record - ios

I have records that are added, updated. Then sync them with server.
According to server response, if one of them fail to update, I would like to have that NSManagedObject to previous value. As I research, UndoManager works as stack, so I can't find any record with Id and undo that record, am I right?
And finally, what would you suggest for this issue?

You could track your objects by introducing your own ID attribute and syncing that with the server. I think this is a solid and robust design - I have used it many times without problems.
Apple does provide an objectID with each managed object, but this is really meant to ensure consistency of data across different managed object contexts. I would not recommend "abusing" this ID for external systems.
Your server could provide the old values (along with the message that it was not updated) and you could write that back into your Core Data store, finding the record using your ID attribute. For more granular change and update management, you could even use a time stamp attribute.

Related

Persist offline changes separately from original data in Core Data

I'm in the middle of adding an "offline mode" feature to an app I'm currently working on. Basically the idea is that users should able to make changes to the data, for example, edit the description of an item, without being connected to the internet, and the changes should survive between app launches.
Each change would normally result in an API request when working online but situation is different in offline mode.
Right now this is implemented by storing all data coming from the API in a Core Data database that acts as a cache. Entities that can be edited by user in addition to normal attributes have the following ones:
locallyCreated - whether the object was created offline
locallyDeleted - object was deleted offline
locallyUpdated - updated
This makes it possible to look for new/deleted/updated objects and send corresponding API requests when doing sync.
This worked well for creating and deleting objects, however, one disadvantage I found with this approach is when new data is retrieved from the API all local changes (i.e. attributes of objects marked as locally updated) are lost, which means that they have to be stored separately somehow.
What would be the best way to approach this problem?
Since you have your locallyUpdated key, the obvious answer is to modify your code that imports server changes, so that it doesn't overwrite changes to any object marked as changed. One way or another you need to avoid overwriting those changes, and you're already keeping a record of which objects have changes, so you already have the tools for a basic solution.
But you'll soon run into the complexity of syncing data. What if the local object has changes on one key, but the incoming data from the server has changes on a different key? You can't resolve that just by knowing that the local copy has changed somehow. Maybe you decide that the server always wins, or that the local copy always wins. Those are easy, if they make sense for your app. If you need to merge changes though, you have some work ahead of you. You would need to record not only a Boolean value indicating that changes were made, but also a list of which keys had changed. This can get complicated, but it's the nature of data syncing.

CoreData concurrency (for sync with CloudKit)

In one of my apps I would like to enable sync via CloudKit. The app itself stores the data using CoreData. I cannot use CoreData+iCloud because I need to be able to track changes which were made which is not possible with the above mentioned setup.
The obvious challenge which I have relates to the fact that CloudKit operations will be happening on the background thread and so I need to support concurrency with my CoreData stack.
Lets assume that I have some CoreData entity which I want to sync with CloudKit. To do background sync I need to create a separate managed object context. When this context fetches changes made to my entity on other devices I want to save these changes.
However, the problem is that during the execution of fetch user could have changed some attributes of a given entity and I am now stuck with this entity having some important information on both contexts. I cannot just choose between which one to save - I need to merge the changes.
Unfortunately, I do not understand which approach I need to take to be able to manually sync these changes. I have already checked the possible techniques (parent-child, multiple contexts, multiple store coordinators) but I still don't understand which approach I need to choose.
Can someone help me to resolve this confusion?
The most ideal solution for me would be such that I would get a conflict whenever sync context tries to merge changes to the main context which also had some changes. In this case I could easily decide which attributes should be updated, but I really don't know how this can be done...
In Apple WWDC 2014 they did a session on just this in effect. Here is the transcript, you can find the video easily too; assuming your Apple Developer.
http://asciiwwdc.com/2014/sessions/231
In in short an Apple engineer goes over a very simple example of using change tokens; an industry standard solution to resolving problem of sorting out data change conflicts.
The Ray Wenderlich web site also has some excellent VIDEOs on cloud kit and core data, it is a subscription service, although well worth the money at just 20$ a month, and no I have no affiliation with him; I'm just a subscriber. Google him and your find his website.

The best way to handle erratic data on iOS

I am working on an application where I have a connection to a database. The database contains from 300MB to 4GB worth of data as each customer has their own database. My issue that I am having is in gathering the data, because of the potential database size, just downloading and storing the information locally isn't possible. The data can get quite complex and can vary. For an example:
A customer has a Job and they want to search for that job from the app.
I then fetch a list of jobs matching the search criteria.
The customer sees the job they want to view and I start the gathering process.
This job can potentially touch many tables, sometimes repeatedly..
There is the jobs table, a relational table to map to a person. Then there is another table that contains non-customer relational information, then there are calendar events associated to the job, which in tun can associate different people. Then there are emails attached to the job, which in turn can bring in additional people and events.
So I have a working model that gathers all of this information. The problem I have is that I cannot figure out a great method of signaling to my view that the data is completely downloaded. My initial thought was to use the NotificationCenter to message when the certain parts of the task were finished, allowing the core Job object to notify the view when everything was complete.
I know this is a pretty generalized question, but I'm honestly stumped as to how to take an unknown number of table results and translate that into a notice that my app can actually use.
My initial recommendation would be Core Data. It's designed for this kind of problem. No, I'm not saying to download the entire database into Core Data. I'm saying to use Core Data to manage your object model, because that's what it's good at.
As you receive data from the server, compose it into NSManagedObjects and stick them in the data store. On the UI side, create an NSFetchedResultsController to keep you informed as the data updates asynchronously. You don't necessarily need to persist this store. You could just keep it in memory and throw it away whenever you're done with the query, but keeping it on disk could be a nice caching solution. Again, don't think of Core Data as "a local database." Think of it as a model persistence engine that you can query for objects.
One advantage of this model is that you can provide the best available data to the user as it becomes available. But say you really don't want to get the information until it's all available. That's fine, too. Just let the network side keep updating its context, and then only save it when everything's complete. That way NSFetchedResultsController gets a single atomic update. The nice things with Core Data is that it has these concepts built in, so you can adjust your update strategy without requiring massive redesign.
The Notification Center will work great for this.
Post the notification at logical points in your data load to trigger a UI update for your users.

Core Data confusion in retrieving records

I'm currently building a Core Data app and I've hit a snag. I guess here's some context on the schema:
The app is to keep track of a therapist's session with her clients. So the schema is organized thus: there's a table of clients, clients have sessions, sessions have activities, and activities have metrics. In the app these metrics translate to simple counters, timers, and NSSliders.
The crux is that the client wants to be able to insert previously made activities into new sessions for new clients. So, I've tried just doing a simple fetch request and then moved on to an NSFetchedResultsController. I keep running into the issue that since Core Data is an object graph, I get a ton of activity entries with virtually the same data. The only differentiating property would be whatever the session is (and if you want to go further back, the client itself).
I'm not sure if this is something I need to change in the schema itself, or if there's some kind of workaround I can do within Core Data. I've already tried doing distinct fetch results with the NSFetchedResultsController by using the result type NSDictionaryResultType. It kind of accomplishes what I want but I only get the associated properties of the entity, and not any children entities associated with it (I need those metrics, you see).
Any help is appreciated, and I can post code if desired even though I don't really have a specific coding error.
I don't see the problem. If you modeled things with the Client, Session, Activity, and Metric entities, each having a to-many relationship to the one to its right and to-one/to-many inverse relationship to the one to its left (in the order I listed the entities), there is nothing stopping you from adding a particular activity into another session (of another client), is it?
Maybe I'm misunderstanding the question.
Just use a simple NSFetchRequest and set the predicate for exactly what you are looking for. You can set the fetch limit if you are getting too many results but your question doesn't exactly sounds like a question IMO.
I believe what you are looking for is an NSPredicate to narrow your results down. Once you fetch a specific object you can assign any relation or attribute to that object easily with dot notation then save the context.

Optimal way of syncing Core Data with server-side data?

I have what I would presume is a very common situation, but as I'm new to iOS programming, I'm not sure of the optimal way to code it.
Synopsis:
I have data on a server which can be retrieved by the iPhone app via a REST service. On the server side, the data is objects with a foreign key (an integer id number).
I'm storing the data retrieved via REST in Core Data. The managed objects have an "objId" attribute so that I can uniquely identify the managed objects in the rest of my code.
My app must always reflect the server data.
On subsequent requests made to the server:
some objects may not be returned, they have been deleted on the server - in which case I need to delete the corresponding objects from Core Data - so that I'm reflecting the state of the server correctly.
some objects have attributes which have changed, therefore the corresponding managed objects need updating with the new data.
my solution - and question to you
To get things going in my app, I made the easiest solution of deleting all objects in Core Data, then adding all new objects in, created with the latest server side data.
I don't think this is the best way to approach it :) As I progress on with my app, I now want to link up my tableview with NSFetchedResultsController, and have realised that my approach of deleting everything and re-adding is not going to work any more.
What is the tried and trusted way of syncing Core Data with server side data?
Do I need to make a fetch request for each object id I get back from the server, and then update the object with the new data?
And then go through all of the objects in core data and see which ones have not been updated, and delete those?
Is that the best way to do it? It just seems a little expensive to do a fetch for each object in Core Data, that's all.
Pseudo code is fine for any answers :)
thanks in advance!
Well, consider your download. First, you should be doing this in a background thread (if not, there are lots of SO posts that talk about how to do that).
I would suggest that you implement what makes sense first, and then, after you can get valid performance data from running Instruments, consider performance optimization. Of course, use some common sense on "easy" performance stuff (your design can take care of the big ones easily enough).
Anyway, get your data from the online resource, and then, for each object fetched, use the "unique object id" to fetch the object from core data. You know there is only one object with that ID, so you can set fetchLimit to 1 on your fetch request. You can also configure your "object id" attribute to be an INDEX in the database. This way, you get the fastest search from the underlying database, and it knows to stop looking once it finds your one object. This should be pretty snappy.
Now you have your object. Change any attributes necessary. Save, rinse, and repeat.
Furthermore, for several reasons, you may want to know when objects were last updated. I'd suggest adding a timestamp to each object that gets changed with the current time every time an object is changed. This will also help in deleting objects. Since your online database does not tell you which objects are deleted, you must have some way to know that an item is "old and no longer needed."
An easy way to do this is to remember the time you started your update. After processing all objects from the download, you now have a way to find all the objects that were deleted from the online database. Basically, any object with a "last update" timestamp before the time you began the update should be removed (since they were not added or modified in the last update). You can also index the database on this field, which will make finding those objects faster - unless your database is huge, I'd wait to see what Instruments has to say about this one though.

Resources