Repair existing CoreData store for 'NSObjectInaccessibleException' - ios

I've come across an existing CoreData app that had incorrect relationships/delete rules set which led to the classic "'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for" when accessing a parent object with children that no longer exist. The relationships and delete rules have been fixed although the existing database still has parent objects that point to children that no longer exist which leads to the fault and a crash.
Is there a way to scan the database and delete references to objects that no longer exist? I have done this manually in the sql file but I need a programatic approach to fix user's existing databases on startup.

Related

Simperium iOS CoreData Relationships Randomly Null

I am currently having an issue with Simperium on iOS with CoreData. Upon launching the app for the first time, Simperium tries to sync with CoreData.
Sometimes it will work 100% correctly, and other times it will set some of the relationships to "nil" despite the Simperium data on the server NOT saying that. This is then NEVER fixed unless I re-install the entire application. And then I roll the dice again to see if the relationships are linked properly on startup.
I cannot find a pattern in this. The relationships that are nil are completely random. Sometimes this entity over here will have nil relationships, and then the next time I try it a different entity will have nil relationships.
All relationships are optional and there is nothing wrong with my CoreData file.
Has anybody had an issue like this? I found the exact same issue in a bug from 2014, but it's been forever since then.
Exact same issue I am having can be found here: https://github.com/Simperium/simperium-ios/issues/250
Side Note: If you read that issue, he also clarifies that he has a problem with editing the CD file WHILE Simperium is syncing, causing nil relationships. Has anybody confirmed this happening? If so this might be my problem.
It's highly likely that this glitch is caused by the scenario you've described (editing the CD file while Simperium is Sync'ing).
Core Data deals, internally, with locks to maintain data integrity. Accessing the sql storage directly might result in data corruption (i'm assuming you're editing the file via either a Firefox plugin, in the simulator, or accessing the file via a Filesystem API).
Please, try to reproduce the issue without accessing the Core Data's sqlite file directly (always go thru NSPersistentStoreCoordinator / NSManagedObjectContext).
If you do succeed, and there's effectively a bug, we'd love to get it fixed! (In which case, please, open an issue the main repository, including as many details as possible).
Thanks for your interest in Simperium!

Simperium duplicates existing records

When my iOS app is started for the first time, it initializes a few entities with default data. The same process is completed for every device of the same user when the app is first installed on the device. This leads to a problem with Simperium, because, even if I have a constraint on one of the entity's attributes, it create duplicates. How can avoid that from happening? Is there a way to make Simperium skip objects locally initialized ? Or to prevent it from inserting duplicates?
Would setting the simperiumKey of the entity to a custom constant value which is the same for all the devices of the user fix the issue? I mean, would that prevent Simperium from importing the very same entity from different devices that would produce duplicates and sync conflicts ?
Because I tried that option and it looks like the buckets on Simperium Server are ok, but when I dispose the view and I open it again the entities are not there anymore. When I restart the app they are back again... it looks like they desappear to reappear after resetting the app.. very strange.
UPDATE
The problem is that I get duplicated entities if I try to insert the same entity from different device. For example. When I setup my app for the first time, I have a function that initializes CURRENCY entities with codes and other things. The same operation would be done from a different device if the user decides to install the app on a new one. In this case, because the new device will initialize again the CURRENCY entities, those info will generate duplicates and conflict errors. I need a way to make Simperium understand that the entities locally inizialized in the devices must not be duplicated. I would remove the inheritance from SPManagedObject in order to stop Simperium from syncing the entities, but in their turn they have relations with other entities and that would definitively create problems with Simperium, because it will try to sync entities which have relations with objects not inheriting from SPManagedObject. Hope you now have a more clear idea.
After reading various posts on this subject I've understood that not signing out (that means: not calling signOutAndRemoveLocalData) and setting custom simperiumKey-s would prevent data duplication. I tested this solution and it apparently looks good. My app needs Simperium to sync data with other devices of the same user, but it mainly works with the local CoreData database. I hope this solution is good for this scenario.

Objects of wrong entity fetched from CoreData relationship

I have a rather big CoreData Model which suddenly behaves weirdly. There are three relevant entities:
User
Story
Group
User has a to-many relationship named stories to Story objects and a to-many-relationship groups to Group objects. Both also have an appropriate inverse. I use mogenerator to generate convenience accessors for all the properties and relationships. Both are ordered to-many relationships.
Now it sometimes happens that when I ask the user object for its groups (like user.groups) that I actually get an NSOrdered set of XNGStory objects. In the few cases that I have observed this they were the same objects that are returned by user.stories. The two sets weren't the same (different pointers), but their contents were (I checked by calling array] valueForKeyPath:#"objectID.URIRepresentation"] on them and got the same URIs in the same order both cases).
It feels like there is some way in my app that stores stories in the groups relationship but I checked the code and there are only Groups-related classes touching groups relationship and only story-related classes touching the story-relationship. Also there is no interaction or relationship between the two they are used in completely separate parts of the app, so I have no idea how this kind of data corruption can happen.
Is there any reason why such a data corruption might happen? I would have expected CoreData to complain when I store objects in the wrong kind of relationship accidentally.
One more thing: So far we were only able to reproduce it on iOS 10.
Things we tried:
We suspected a memory issue first (because an object of the wrong type turns up where it's not supposed to be), but running with Address Sanitizer and NSZombies enabled didn't reveal any issues. Also, once the issue occurs it persists over app restarts and using the debugger to poke around in the objects also shows a consistent(ly wrong) data model so it's not just one pointer that's wrong.
Deactivated WAL (issue still exists)
Looked at sqlite file (both with and without WAL) after crash with Core Data Editor: It displays Group-objects for the groups relationship, so all seems to be good on the sqlite-database-level -> Somehow the relationships get mixed up when loaded from the file.
As best I can tell, this seems to be an iOS 10 bug.
We have a similar problem with an app that uses Core Data - a User object has a relationship Addresses (to Address objects) and Friends (to other User objects).
For some reason under iOS 10 Core Data occasionally chooses to return the Address objects in the Friends relationship.
We've regression tested back to iOS 9 and this issue doesn't occur.
There's an open bug on Open Radar for the same issue - 26826183. Interestingly we've only started seeing this issue under 10.0.2 but that Radar report was based on an early iOS 10 Beta.
We have tested this extensively and also conclude that it is an iOS 10 bug. It also does not seem to be related to any of our code, just the data model. We were able to recreate the issue in an example project using our data model, Xcode 8's code generation and iOS 10's new NSPersistentContainer (before it broke using mogenerator and the "old way of setting up a CoreData stack").
To reproduce the issue we do the following:
At startup, if there is no User-object, create a User-object and add 10 Group objects to it and save.
Then add a Story to it, still on the main-context, and save.
Kill the app (via iOS swipe-up or simply via pressing "stop" in Xcode)
Start the app again. On startup, fetch the User object, add a Story object to to the stories relationship and save.
Check the user.groups relationship on a background or main context. It contains XNGStory objects now, although it should still have XNGGroup objects.
I filed a Radar with the example project. OpenRadar is here (without the example project, as it contains our data model).
The issue seems to be highly dependent on the data-model setup. It occurs when all-but-on relationships on our User entity are ordered. If all are ordered, it doesn't occurs, neither does it occur if two or more are unordered.

"Core Data could not fulfill a fault for..."

My app is in App Store now. From 2 users I got a lot of crashes. I track them with Crashlytics. The screenshots from my issue are following:
Core Data could not fulfill a fault for... WLWishlist
What does it mean? Is it sth wrong with WLWishlist or objects with relationships to WLWishlist?
Can you help me debug this?
Below is the line 82 for the file when the crash appeared:
That NSObjectInaccessibleException should give you a clue, as should the CoreData could not fulfill a fault for ... /WLWishlist/.... Somewhere you create an instance of a WLWishlist which is an NSManagedObject subclass. You might be creating this instance directly or perhaps it is a related entity from some other managed object. This particular instance is a fault; it's properties have not yet been loaded from the persistent store but should be retrievable on demand. It is expected that your persistent store will be able to fulfill any fault and provide the values of that object's properties. In this case that was not possible and so the persistent store throws an exception.
One way this might happen is if you attempt to fulfill a fault for an object which has been deleted from the persistent store. Without knowing more about what your application is doing it's impossible to say how you got into this situation.

How to make NSManagedObject not fault?

I'm currently debugging a big project written by another developer. The project uses CoreData which i am very new to. I'm having a crash which happens due to the fact that some NSManagedObject is being a fault(i have poor understanding of what fault is) and i would like to convert the object into "not fault" and see if it helps. Reading documentation made me think that to make object not fault is equal to to fire fault (again i have poor understanding of what is "to fire"), so i decided to fire it by calling any method on it, and this method is hasChanges since it's not in the list of methods which don't fire fault. However even after calling this method the object still remained being fault. Can anyone give me an example of how to convert NSManagedObject into "not fault" state?
The exception you mentioned in a comment is:
Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1f0627a0 <x-coredata://E40418A0-A8E5-4340-865F-A9DA2E0095DD/CoreObject/p288>''
The "could not fulfill a fault" message indicates that this is not simply a problem with firing the fault. In fact there are no special steps to fire a fault-- you just access the attributes, and if necessary, the fault fires automatically.
What this error is telling you is that you're doing something that causes a fault to fire, but that Core Data can't find any record of the instance you're using. This can happen in certain scenarios, for example:
Fetch an object, and leave it as a fault (i.e. don't access any attributes)
Delete it from Core Data, but keep a reference to the object (maybe in an instance variable)
Save changes
Try to access an attribute on the object you fetched in step 1.
At this point Core Data would normally use the object to look up the attribute value. But you already deleted it, so the attribute data is gone. Core Data throws this exception.
There are some other scenarios that can trigger this-- calling reset on a managed object context while keeping previously fetched objects around, or removing the persistent store (so that the data still exists in the store file, but the file isn't loaded anymore). In general it means Core Data is trying to look up data on an object that's no longer valid.
What you need to do:
Figure out which object is causing this. Setting an exception breakpoint, so that the debugger loads just as the crash is happening, is probably a good idea.
Get rid of that object. Preventing the fault from firing might prevent this specific crash, but as long as you still have this object, it's like a land mine in your app. It will explode and crash the app as soon as you touch it.
Figure out why you have invalid managed objects lurking in memory. Maybe you should have gotten rid of them earlier? Maybe you're accidentally deleting something you don't want to delete? For some reason you're keeping managed objects around when they're no longer valid. This is the core problem that's causing your trouble.
Faulting is the process whereby an object and/or its properties are fetched from the database on-demand.
For example, if you attempt to access a property or relation on some object, person.name, that object may not have its data in-memory, and it has to be fetched from the underlying data store. This is a fault.
Faulting is a normal part of how Core Data works, and should not be related to crashes. An object will be unfaulted when you try to access its properties and relations.

Resources