prevent CoreData from returning cached results - iphone

I am querying for a Client NSManagedObject via Core Data in my app and displaying the result in a simple details UIView pushed onto my UINavigationsController stack - nothing complex or out of the ordinary.
When I navigate back out of this details page for this Client object I am also checking for updates to my Clients in the background and if there are some I update my data store quietly in a background thread on a different managed object context. This is working and I see the changes to the data appear in my Core Data store via my SQLite Database Browser so I know the data has been updated.
The problem is when I navigate back to that same Client I will not see the changes unless I completely close my app or select a different Client and then go back to the one I know has changes. It seems the last Client object fetch request is being cached by my managed object context.
How do I prevent this from happening?

You need to register for NSManagedObjectContextDidSaveNotification passing the background NSManagedObjectContext as the object and then call mergeChangesFromContextDidSaveNotification: when the notification fires on your NSManagedObjectContext on the main thread.
This is specified in the Core Data Programming Guide: Concurrency with Core Data. And dont forget to refresh the data in your view (if it is a table view call reloadData)

This may be an overly simple solution, but are you reloading your data in viewWillAppear: ?
- (void)viewWillAppear
{
[self.tableview reloadData];
}

Related

Open UIManagedDocument take too much time

Recently, I'm working with a timetable app in iOS, and i get trouble with Core Data.
My app has a main user interface kind of like the original calendar app created by Apple, and i save all my events data in the Core Data database.
I create a UIManagedDocument in order to fetch data from database by using its NSManagedObjectContext, and everything works just fine.
However, i need to use the NSManagedObjectContext to fetch data several times in several different view controllers during the runtime. And every time i do this, i need to reopen the UIManagedDocument, but open the document take too much time(it may take 2 seconds or even more, i have to display a spinner in view).
So here are my questions:
What's the right way to open a managedDocument?(I mean like open it during the lunch image time?)
Is there a way to only open the managedDocument once and keep it open during runtime?(So i can use its context all the time)
Does data store in the managedDocument i create?(I found that if i delete the document, data was gone)
Thanks.
You will get lots of different opinions on how to do this but basically you should only have to open the document once. This is done by some object that does the work and stores it so it can return it again when asked by a different view controller.
Many people create singleton's for this or put it in the App Delegate. I have implemented a protocol that lets me put it where ever it is convenient for a particular application without my other code having to know anything about the object that returns the information. As long as it responds to the protocol it can be the App Delegate, a singleton class, or any other object type.
See importing AppDelegate
The protocol that I put in the above example just returns information about where the database is. In my actual implementation I have an openDatabase method with a call back to let me know when it is done plus automatic initialization and updating methods.
You can also improve your performance by having the open operation happen off the main thread. This keeps your UI responsive but does not show the data any faster and you have to be carefull about managed object contexts and the threads they are in.
Good luck...

Core Data - How to disable faulting mechanism

Is there any way to tell Core Data to disable its memory management? I have NSManagedObjects in a view that don't get saved to a context for a very long time. The managed objects are in a table view. The user will see that view first, then navigate away to another view, spending lots of time there before returning to the table view. I am getting this exception: “NSObjectInaccessibleException - CoreData could not fulfill a fault”
To disable faulting when using an NSFetchRequest to get core data entities, just add the line [request setReturnsObjectsAsFaults:NO] before you execute the request.
Read more details here
A Core Data fault fulfillment error usually means that you are holding on to an object that has been faulted and another thread has deleted one of the children objects and committed to the persistent store. So when the original thread goes back to fulfill the fault, there isn't anything there anymore.
One of the things you can do is have your other views listen to NSManagedObjectContextDidSave and NSManagedObjectContextWillSave to react to the change as it is occurring (i.e. reload the data fresh), so you don't wind up with bad objects in the cache.

CoreData relationships are nil after mergeChangesFromContextDidSaveNotification

Having some truly strange behavior with Core Data on iOS.
I have a NSManagedObjectContext for my main thread used to read data from a SQLLite persistent store and display it to the user. I also have background processes managed by an NSOperationQueue. These background processes create an NSMangedObjectContext, fetch data from remote servers, and persist that data to the local Core Data store.
I have registered for NSManagedObjectContextDidSaveNotification and when I receive those notifications I call mergeChangesFromContextDidSaveNotification on the main thread's NSManagedObjectContext (passing the notification object as an argument).
This is all very standard and the way that all the Core Data document suggest that you deal with multi-threading.
Up until recently, I have always been inserting new objects into the data store and not modifying objects in the data store. This worked fine. After a background thread writes new data, the merge occurs, I send a notification to the UIController and redraw my display. The display draws correctly.
Recently I have made a change, and the background thread is both inserting and modifying objects. But all the rest of the pattern remains the same. Now after the merge, the data in my main thread’s NSManagedObjectContext is corrupted. If I try to query for objects, I get nothing back. If I try to examine objects which I already have a reference to all their relationships are nil (not faults but nil). I have checked the SQLLite database and the data is all there.
The only solution seems to be to reset the NSManagedObjectContext which isn’t acceptable given the architecture of the applicaiton.
Ok, a final bit of strangeness. If my background thread is only updating attributes (primitves) then I don’t get this strange behavior. However, if I update the relationships themselves, then I get these empty fetch request results and nil’d out relationships.
What am I missing?
Ok, I figured out my problem.
My problem is the way that mergeChangesFromContextDidSaveNotification merges changes from one thread to another. As I have read several times, it does not do a playback of the changes you made but rather merges the final state together. I didn't realize the ramifications of this. In my background thread, I am deleting an object, but before I delete it I nil out some of its relationships because they are marked as cascade delete and I don't want them deleted.
Apparently when I merge those deleted objects into the main context, Core Data still enforces the delete rules and cascade deletes the related objects in the main context.
I changed my delete rules and now my relationships is not getting incorrectly nil'd out. It is unfortunate that I have a relationship which in some cases I want cascade deleted and in others I don't. I will have to work on that.

How to sync CoreData and a REST web service asynchronously and the same time properly propagate any REST errors into the UI

Hey, I'm working on the model layer for our app here.
Some of the requirements are like this:
It should work on iPhone OS 3.0+.
The source of our data is a RESTful Rails application.
We should cache the data locally using Core Data.
The client code (our UI controllers) should have as little knowledge about any network stuff as possible and should query/update the model with the Core Data API.
I've checked out the WWDC10 Session 117 on Building a Server-driven User Experience, spent some time checking out the Objective Resource, Core Resource, and RestfulCoreData frameworks.
The Objective Resource framework doesn't talk to Core Data on its own and is merely a REST client implementation. The Core Resource and RestfulCoreData all assume you talk to Core Data in your code and they solve all the nuts and bolts in the background on the model layer.
All looks okay so far and initially I though either Core Resource or RestfulCoreData will cover all of the above requirements, but... There's a couple of things none of them seemingly happen to solve correctly:
The main thread should not be blocked while saving local updates to the server.
If the saving operation fails the error should be propagated to the UI and no changes should be saved to the local Core Data storage.
Core Resource happens to issue all of its requests to the server when you call - (BOOL)save:(NSError **)error on your Managed Object Context and therefore is able to provide a correct NSError instance of the underlying requests to the server fail somehow. But it blocks the calling thread until the save operation finishes. FAIL.
RestfulCoreData keeps your -save: calls intact and doesn't introduce any additional waiting time for the client thread. It merely watches out for the NSManagedObjectContextDidSaveNotification and then issues the corresponding requests to the server in the notification handler. But this way the -save: call always completes successfully (well, given Core Data is okay with the saved changes) and the client code that actually called it has no way to know the save might have failed to propagate to the server because of some 404 or 421 or whatever server-side error occurred. And even more, the local storage becomes to have the data updated, but the server never knows about the changes. FAIL.
So, I'm looking for a possible solution / common practices in dealing with all these problems:
I don't want the calling thread to block on each -save: call while the network requests happen.
I want to somehow get notifications in the UI that some sync operation went wrong.
I want the actual Core Data save fail as well if the server requests fail.
Any ideas?
You should really take a look at RestKit (http://restkit.org) for this use case. It is designed to solve the problems of modeling and syncing remote JSON resources to a local Core Data backed cache. It supports an offline mode for working entirely from the cache when there is no network available. All syncing occurs on a background thread (network access, payload parsing, and managed object context merging) and there is a rich set of delegate methods so you can tell what is going on.
There are three basic components:
The UI Action and persisting the change to CoreData
Persisting that change up to the server
Refreshing the UI with the response of the server
An NSOperation + NSOperationQueue will help keep the network requests orderly. A delegate protocol will help your UI classes understand what state the network requests are in, something like:
#protocol NetworkOperationDelegate
- (void)operation:(NSOperation *)op willSendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
- (void)operation:(NSOperation *)op didSuccessfullySendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
- (void)operation:(NSOperation *)op encounteredAnError:(NSError *)error afterSendingRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
#end
The protocol format will of course depend on your specific use case but essentially what you're creating is a mechanism by which changes can be "pushed" up to your server.
Next there's the UI loop to consider, to keep your code clean it would be nice to call save: and have the changes automatically pushed up to the server. You can use NSManagedObjectContextDidSave notifications for this.
- (void)managedObjectContextDidSave:(NSNotification *)saveNotification {
NSArray *inserted = [[saveNotification userInfo] valueForKey:NSInsertedObjects];
for (NSManagedObject *obj in inserted) {
//create a new NSOperation for this entity which will invoke the appropraite rest api
//add to operation queue
}
//do the same thing for deleted and updated objects
}
The computational overhead for inserting the network operations should be rather low, however if it creates a noticeable lag on the UI you could simply grab the entity ids out of the save notification and create the operations on a background thread.
If your REST API supports batching, you could even send the entire array across at once and then notify you UI that multiple entities were synchronized.
The only issue I foresee, and for which there is no "real" solution is that the user will not want to wait for their changes to be pushed to the server to be allowed to make more changes. The only good paradigm I have come across is that you allow the user to keep editing objects, and batch their edits together when appropriate, i.e. you do not push on every save notification.
This becomes a sync problem and not one easy to solve. Here's what I'd do: In your iPhone UI use one context and then using another context (and another thread) download the data from your web service. Once it's all there go through the sync/importing processes recommended below and then refresh your UI after everything has imported properly. If things go bad while accessing the network, just roll back the changes in the non UI context. It's a bunch of work, but I think it's the best way to approach it.
Core Data: Efficiently Importing Data
Core Data: Change Management
Core Data: Multi-Threading with Core Data
You need a callback function that's going to run on the other thread (the one where actual server interaction happens) and then put the result code/error info a semi-global data which will be periodically checked by UI thread. Make sure that the wirting of the number that serves as the flag is atomic or you are going to have a race condition - say if your error response is 32 bytes you need an int (whihc should have atomic acces) and then you keep that int in the off/false/not-ready state till your larger data block has been written and only then write "true" to flip the switch so to speak.
For the correlated saving on the client side you have to either just keep that data and not save it till you get OK from the server of make sure that you have a kinnf of rollback option - say a way to delete is server failed.
Beware that it's never going to be 100% safe unless you do full 2-phase commit procedure (client save or delete can fail after the signal from the server server) but that's going to cost you 2 trips to the server at the very least (might cost you 4 if your sole rollback option is delete).
Ideally, you'd do the whole blocking version of the operation on a separate thread but you'd need 4.0 for that.

iphone threading speed up startup of app

I have an app that must get data from the Sqlite database in order to display the first element to the User.
I have created a domain object which wraps the DB access and is a thread safe singleton.
Is this following strategy optimal to ensure the fastest load given the iPhone's file access and memory management capabilities in threaded apps:
1) In the AppDelegate's FinishedLaunching event the very first thing I do is create the domain singleton within a new thread. This will cause the domain object to go to Sqlite and get the data it needs without locking the UI thread.
2) I then call the standard Window methods to add the View and MakeKeyAndVisible etc.
Is there an earlier stage in the AppDelegate where I should fire off the thread that creates the Domain Object and accesses Sqlite?
Heh, you can go all the way back to the app's execution entry point and create your own thread before invoking UIApplicationMain... that's overkill.
applicationDidFinishLaunching is the best place to do it, if you're worried about fast loading a better approach would be to cache the data in your plist or NSUserDefaults and then update it a couple hundred millisecs later when the DB is ready.