CoreData fault - how to get data - ios

I've researched tons of questions and documents about CoreData returning faults instead of actual values:
Relationship 'whiskers' fault on managed object (0xb7abab0)
This happens when I'm trying to get the count for the number of whiskers, such as:
self.numWhiskersLabel.text = [NSString stringWithFormat:#"%d", cat.whiskers.count];
Even if I try to log the whiskers set directly I still get a fault:
NSLog(#"whiskers: %#", cat.whiskers);
I understand that "Core data will not return full object until there is a need to access the actual value of that object. Each of your returned objects will be a 'fault' until this point." That's great, but there is a need to access the actual value at this point. I need the value right now! So how do I get out of this oxymoron? How can accessing the count of a Set not be considered needing the value?

I didn't get any feedback from my comment so I'm just going to assume whiskers is a set of NSManagedObjects
The set wont be loaded initially because internally it's coming from another table in the db. When you access .whiskers.count it still doesn't need to go and get the data yet, because all you're wanting is the number of whiskers in the set.
When you pull a whisker out of the set, then it will be faulted, try doing
NSLog(#"whiskers: %#", [cat.whiskers.anyObject anyProperty]);
That should give you a loaded NSManagedObject.

This is an error condition. Something is wrong with that NSManagedObject instance. Either it was deleted before you accessed it or you are trying to touch it from the wrong thread.
Please edit your question and show the code that is accessing that NSManagedObject.
Also, what happens when, in the debugger, you just do a po cat? Do you see the full Cat object or is that giving a fault error as well?

Related

Why does storing a reference to an NSManagedObject prevent it from updating?

This question is poorly phased but this can be better explained in code.
We have a Core Data Stack with private and main contexts as defined by Marcus Zarra here: http://martiancraft.com/blog/2015/03/core-data-stack/
We call a separate class to do a fetch request (main context) and return an array of NSManagedObjects:
NSArray *ourManagedObjects = [[Client sharedClient].coreDataManager fetchArrayForClass:[OurObject class] sortKey:#"name" ascending:YES];
We then do some processing and store a reference:
self.ourObjects = processedManagedObjects
Our view contains a UITableView and this data is used to populate it and that works just fine.
We change the data on our CMS, pull to refresh on the UITableView to trigger a sync (private context) and then call this same function to retrieve the updated data. However, the fetch request returns the exact same data as before even though when I check the sqlite db directly it contains the new data. To get the new values to display I have to reload the app.
I have discovered that if I don't assign the processedManagedObjects to self, the fetch request does indeed return the correct data, so it looks like holding a reference to the NSManagedObject stops it from getting new data from the main context. However I have no idea why that would be.
To clarify, we're pretty sure there's nothing wrong with our Core Data Stack, even when these managed objects are not being updated, other are being updated just fine, it's only this one where we store a local reference.
It sounds like what's going on is:
Managed objects don't automatically update themselves to reflect the latest data in the persistent store when changes are made via a different managed object context.
As a result, if you keep a reference to the objects, they keep whatever data they already had.
On the other hand if you don't keep a reference but instead re-fetch them, you get the new data because there was no managed object hanging around with its old data.
You have a few options:
You could keep the reference and have your context refresh the managed objects, using either the refresh(_, mergeChanges:) method or refreshAllObjects().
If it makes sense for your app, use an NSFetchedResultsController and use its delegate methods to be notified of changes.
Don't keep the reference.
The first is probably best-- refreshAllObjects() is probably what you want. Other options might be better based on other details of your app.
Try setting the shouldRefreshRefetchedObjects property of the fetch request to true. According to the documentation:
By default when you fetch objects, they maintain their current property values, even if the values in the persistent store have changed. Invoking this method with the parameter true means that when the fetch is executed, the property values of fetched objects are updated with the current values in the persistent store.

Core Data fetch predicate nil check failing/unexpected results?

I have a Core Data layer with several thousand entities, constantly syncing to a server. The sync process uses fetch requests to check for deleted_at for the purposes of soft-deletion. There is a single context performing save operations in a performBlockAndWait call. The relationship mapping is handled by the RestKit library.
The CoreDataEntity class is a subclass of NSManagedObject, and it is also the superclass for all our different core data object classes. It has some attributes that are inherited by all our entities, such as deleted_at, entity_id, and all the boilerplate fetch and sync methods.
My issue is some fetch requests seem to return inconsistent results after modifications to the objects. For example after deleting an object (setting deleted_at to the current date):
[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:#"deleted_at==nil"]];
Returns results with deleted_at == [NSDate today]
I have successfully worked around this behavior by additionally looping through the results and removing the entities with deleted_at set, however I cannot fix the converse issue:
[CoreDataEntity fetchEntitiesWithPredicate:[NSPredicate predicateWithFormat:#"deleted_at!=nil"]];
Is returning an empty array in the same conditions, preventing a server sync from succeeding.
I have confirmed deleted_at is set on the object, and the context save was successful. I just don't understand where to reset whatever cache is causing the outdated results?
Thanks for any help!
Edit: Adding a little more information, it appears that once one of these objects becomes corrupted, the only way get it to register is modifying the value again. Could this be some sort of Core Data index not updating when a value is modified?
Update: It appears to be a problem with RestKit https://github.com/RestKit/RestKit/issues/2218
You are apparently using some sintactic sugar extension to Core Data. I suppose that in your case it is a SheepData, right?
fetchEntitiesWithPredicate: there implemented as follows:
+ (NSArray*)fetchEntitiesWithPredicate:(NSPredicate*)aPredicate
{
return [self fetchEntitiesWithPredicate:aPredicate inContext:[SheepDataManager sharedInstance].managedObjectContext];
}
Are you sure that [SheepDataManager sharedInstance].managedObjectContext receives all the changes that you are making to your objects? Is it receives notifications of saves, or is it child context of your save context?
Try to replace your fetch one-liner with this:
[<your saving context> performBlockAndWait:^{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"CoreDataEntity"];
request.predicate = [NSPredicate predicateWithFormat:#"deleted_at==nil"];
NSArray *results = [<your saving context> executeFetchRequest:request error:NULL];
}];
First, after a save have you looked in the store to make sure your changes are there? Without seeing your entire Core Data stack it is difficult to get a solid understanding what might be going wrong. If you are saving and you see the changes in the store then the question comes into your contexts. How are they built and when. If you are dealing with sibling contexts that could be causing your issue.
More detail is required as to how your core data stack looks.
Yes, the changes are there. As I mentioned in the question, I can loop through my results and remove all those with deleted_at set successfully
That wasn't my question. There is a difference between looking at objects in memory and looking at them in the SQLite file on disk. The questions I have about this behavior are:
Are the changes being persisted to disk before you query for them again
Are you working with multiple contexts and potentially trying to fetch from a stale sibling.
Thus my questions about on disk changes and what your core data stack looks like.
Threading
If you are using one context, are you using more than one thread in your app? If so, are you using that context on more than one thread?
I can see a situation where if you are violating the thread confinement rules you can be corrupting data like this.
Try adding an extra attribute deleted that is a bool with a default of false. Then the attribute is always set and you can look for entities that are either true or false depending on your needs at the moment. If the value is true then you can look at deleted_at to find out when.
Alternatively try setting the deleted_at attribute to some old date (like perhaps 1 Jan 1980), then anything that isn't deleted will have a fixed date that is too old to have been set by the user.
Edit: There is likely some issue with deleted_at having never been touched on some entities that is confusing the system. It is also possible that you have set the fetch request to return results in the dictionary style in which case recent changes will not be reflected in the fetch results.

Cannot delete objects in other context

I am facing this problem consistently for over 3 months. I have searched a lot and read related docs and visited many forums but couldn't find working solution. I am getting typical NSManagedObject error while deleting objects. An NSManagedObjectContext cannot delete objects in other contexts.
I tried to go around and tried to delete object using its NSManagedObject ID but to no avail.
NSManagedObjectID *findingsSurveyDataItemApiId = [findingsSurveyDataItemApi objectID];
[self.managedObjectContext deleteObject:[self.managedObjectContext objectWithID:findingsSurveyDataItemApiId]];
Can anyone tell why is above solution still not working?
PS: I have two managed object context in the app.
I guess it might be a misleading error message from Core Data. If the object you want to delete has not yet been saved to the persistent store, objectWithID will not return a valid object, according to the docs:
The data in the persistent store represented by objectID is assumed to exist—if it does not, the returned object throws an exception when you access any property (that is, when the fault is fired).
Use existingObjectWithID:error: instead and check if it returns a non-nil object before trying to delete it.

Can I see the contents of Core Data managed objects in the stack variables area?

I'm developing for iOS 6.1 and I'm learning Core Data.
I've got a managed object abc.myString that I've stuffed a string into:
abc.myString = #"the String";
When I see abc.myString in the variables area, if I look inside, I see only NSManagedObject and if I drill deeper, I see a lot of stuff I don't understand but I don't see my string.
But, after loading abc.myString, I can do this to verify what I put in:
NSLog( #"contents = %#", abc.myString );
Is this expected behavior; that I cannot see into abc.myString in the variables area?
Yes, this is expected behaviour. NSManagedObject doesn't store its' fields in instances variables. Think of NSManagedObject as a front end to the persistent store's data. NSManagedObject doesn't actually store the data it fetches and update the persistent store.
You can still access the data by using the debugger. In the console type the follow to log an object:
po variableNameOrAddressOfTheObjectYouWantToLog
You can also set up break points to do this logging automatically.
Yep, Xcode will not display the contents of the managed object in the variables list, but you can right click & use Print Description of 'xyz' to print the contents into the console.

Sorting a NSSet of NSManagedObjects by a NSDate yields error

I am trying to pull in a RSS feed and sort by pubDate. When I examine the 'updated' property, most of the time it is correct and give me a proper date but when I try to convert from a set to a sorted array, I get random results from the sort. I've tracked this down to the fact that when sort is doing it's comparesion, the property (which is an NSDate, see figure1) is coming in and being compared as a __nscfnumber! (also figure2)
Any help or idea would be much appreciated.
figure1
figure2
I assume the comparator block is just for diagnostic purposes? You don't actually need to supply a comparator for NSDate or any of the provided attribute type classes.
If the debugger is reporting that the date1 object is of a NSNumber-cluster class type, then somewhere a NSCFNumber instance is being assigned to to the updated attribute. The debugger ignores factors like a cast and instead simply asked the object what its class is. If the object says it is a NSCFNumber then it is, regardless of how the code treats it otherwise.
Why that happens, I can't say based on the code provided.
You might try logging the value and class of the updated attribute before you attempt the sort to see if it reports properly. I would also recommend decomposing the entire line. Nesting all those method calls will work of course but it is error prone and hard to debug.

Resources