views:

56

answers:

1

I think I understand the error message: CoreData could not fulfill a fault, but I am not sure how I should deal with it.

We have an application where we use Core Data to persist data returned from a JSON service. Today I am doing the following.

  1. Fetch local object from persistent store and return to UI
  2. Ask server if the object is updated - when I get the answer, I update the Core Data managed object
  3. Update UI with the updated object

The problem is; even if I do not use multi threads I sometimes gets an error when the HTTP request deletes managed objects that my UI has retained. I tried to fetch the objects with returnsObjectsAsFaults to NO. I thought I then could access all the relations and properties of an managed object even if it was deleted (as long as my UI had retained it).

How should I solve this issue?

I thought I could use separate NSManagedObjectContext for read and write. I have made this test:

MyAuthorMO *authorUpdate = [[MyAuthorMO alloc] init]; // I have made this init insert the object into the updateContext
authorUpdate.firstname = @"Hans";
authorUpdate.lastname = @"Wittenberg";
authorUpdate.email = @"[email protected]";

NSManagedObjectContext *updateContext = [[MyCoreManager getInstance] managedObjectContext];

NSError *error = nil;
[updateContext save:&error];

NSManagedObjectContext *readContext = [[MyCoreManager getInstance] readOnlyContext];

NSFetchRequest *fetchRequest = [managedObjectModel fetchRequestFromTemplateWithName:@"authorByEmail" substitutionVariables:[NSDictionary dictionaryWithObject:@"[email protected]" forKey:@"EMAIL"]];
[fetchRequest setReturnsObjectsAsFaults:NO];

NSArray *authors = [readContext executeFetchRequest:fetchRequest error:&error];

MyAuthorMO * readAuthor = [authors objectAtIndex:0];

// Delete the author with update context:
[updateContext deleteObject:authorUpdate];
[updateContext save:&error];

NSLog(@"Author: %@ %@, (%@)", readAuthor.firstname, readAuthor.lastname, readAuthor.email);

The log is outputted just fine as long as I use the readContext for the fetch. If I use the updateContext for the fetch, I get an exception. This looks promising, but I am afraid that I will run into problems at a later stage. Sooner or later I will probably try to access a property that is not fetched completely (a fault). How can I achieve the behaviour I am looking for?

A: 

You shouldn't retain managed objects. Let the context do that for you.

The problem is that managed objects can exist as either faults or actualized objects. When you retain one, you may retain the fault which contains no data. Even if you do retain the actual object, the object may not behave properly once it has been separated from its context.

In order to handle your scenario, you need to a context for the UI and then a context for the server. After either context makes changes, you should merge the context to ensure both are properly updated relative to the store.

Your UI should be configured to reflect the state of data model, you shouldn't have parts of the data model dependent on the state of the UI.

TechZen
I agree with you, but have some follow up questions. I am not retaining the MO, but if I use it somewhere, it could probably be retained without me knowing it. Is it ok to add a MO to NSArray or NSDictionary? If so, then it will be retained...Do I need to merge the context, or could I just wait for the next time I fetch from "read"-context"? I am afraid that my data gets corrupt if I merge two context where the object I am currently rendering are deleted (with "write"-context).
Andi
I was unclear. I meant you shouldn't try to retain a managed object that a context has disposed of. That will get messy. You should merge the context but you will need to freeze the interface while the model updates. Look at how the NSFetchedResultsController handles this exact problem with updating it data while the data is displayed in a table. Freezing the UI is usually completely unnoticed by the user. If you have data that might be deleted while the user is working with it, you need to rethink your design. The user should control such deletions.
TechZen