NSManagedObjectContext refreshObject causes duplicates in NSFetchedResultsController - ios

I have an issue where if I make changes to a core data object, save then refreshing the object, causes my NSFetchedResultsController to show a duplicate object. I think I understand what's going on, but I'm looking for someone to confirm, and also to hopefully give some more detail as to why.
To explain in more dtail I have two entities, Fixture and Position. A Fixture has many Positions, and a Position belongs to only one Fixture. To reproduce the issue I do the following:
Fetch all Positions.
Modify some value (any one) on that objects Fixture. I.E foo.fixture.name = "foobar"
Save the context
Refresh objects by calling context.refreshAllObjects, or context.refreshObject(foo, mergeChanges: false/true).
I have a tableview using a fetched results controller which displays Fixures. After doing the above the tableview will display duplicates for each item (it doesn't matter if I use the delegate methods of the FRC to do the update or I just reload the tableview).
It appears what's happening is that the refresh invalidates the objects that the FRC knows about, while at the same time gets knowledge of another set of objects. If, as step #5, I call frc.performFetch() then the problem goes away.
Other things to note:
No matter how many times I run the code I only get two of each object (I'm using a random button to trigger it for testing).
init(entityName, context) is called on my Fixture subclass as soon as I access the Fixture property of my object during the next code run (i.e after refresh was called).
In my sample everything is taking place on the same context (though it happens with child contexts as well)
To give some more context as to how I got myself in this situation in the first place users can click on a fixture in the list and then ultimately narrow down on a single position a few screens later where they can perform actions that modify the fixture. There are other active areas of the application at this point that are listening to the NSManagedObjectContextDidSaveNotification and I want them to update their objects so they can display the correct data, which is why I was calling refresh.
I've dug around in the docs and I can't see anything that specifically explains my theory that updating the context causes NSFetchedResultsController to have invalid objects. Can anyone shed some light on this behavior?

First, you really should not be overriding -init... on a NSManagedObject. That is one of the methods that you are strongly discouraged from overriding and can very easily be the source of your issues.
Second, your entire UI should be using a single instance of the NSManagedObjectContext that is associated to the main queue and therefore there should only be ONE instance of any particular entity in your UI. If you have multiple contexts you are just making things more complicated for yourself. If you are using a single context keep in mind that no matter how many times you fetch an object against that context you will get the exact same pointer back.
The NSFetchedResultsController will never create objects, it only fetches and provides them for display. Therefore the NSFetchedResultsController is only reporting the fact that you have created this duplication somewhere else in your code.
Now some questions, do these duplicates get pushed down to disk?
Can you see them in the store file on disk?
When you re-launch your application are the duplicates still there?
When you put a print in your custom -init methods on the NSManagedObject; do they fire more than once?

Related

Realm deleted objects strategy and notifications

It's not an issue, but rather just a general question on how realm collection change notifications are dispatched if changes happen in background thread.
So there is a scenario (in Realm Cocoa):
RLMResults of objects of class Foo: RLMObject is fetched from default realm. The list is then transformed into array [Foo] and saved as variable in view controller (does not matter)
Table displays that list of foo objects
Some objects are deleted on background thread
User scrolls the table view, cell is reused, object from array at index x is accessed, but it was deleted on background thread and crash happens, because object was deleted or invalidated. As expected.
To solve that we could use collection notifications and refresh the list when changes occur. As I tried everything works as expected, but isn't it possible that dispatch of cell reuse will occur before notification is dispatched on main thread so that cell setup method will be using invalidated object?
Just tried to explain the question as detailed as possible.
Mainly the question is about situation(s) when data sync with server (not using Realm's mobile platform) is happening on background thread and views, whether they're table cells or any other views, are holding references to could-be-deleted objects. Is it a good practice to check if object was invalidated when trying to do something with the object because it could be deleted on background thread.
I see a couple of solutions:
each time accessing reference of RLMObject subclass object check if it is not invalidated
wrap the object into view model (leaving all the good parts of self-updating model features) which then leaves with another two solutions when trying to change the model:
save object id in view model so that when trying to change it would be possible to fetch object again
have a reference to the object itself having the same problems as the first point
What are the suggestions for this?
EDIT:
Using Results and List sometimes not really possible if the object Foo is complicated. For example when opening details screen of Foo table view with lots of different cells accessing properties of Foo class' object. So on notification about deleted object screen could be dismissed, but as user scrolls isn't it possible that cell could be accessing invalidated object?
Maybe the question is just stupid, over engineered about raise conditions, but I'm curious if it's possible that dispatches on main thread will occur something like: object becomes invalidated (I don't know how that happens in realm internally> then some code which access that object then notification about invalidated object
The list is then transformed into array [Foo] and saved as variable in view controller (does not matter)
Why do you do this? This forces every object in the Results to be materialized into memory, with (potentially expensive) Swift-level object accessors to be created and some db data read from disk. All Realm collections conform to the Swift standard library's CollectionType protocol, meaning that it behaves like other collections such as Array already. Copying out all of the Results elements into an Array will also mean that the array will quickly get out of sync with its underlying data, since Array isn't auto-updating unlike Results.
This easily explains why some of the Realm objects that you copy into the Array are then deleted, which causes the objects to become invalidated, meaning that any subsequent access is a violation of Realm's API.
Long story short, don't copy Results, List or any other Realm collection into an Array.

Child NSManagedObjectContext inconsistent NSFetchedResultsController results after saving

I have a very peculiar.. issue happening with my NSFetchedResultsController.
My setup is using Magical Record, I have a child with the parent set as MR_defaultContext ([NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]).
I create a nested context using the first view controller's managedObjectContext as the parent, use that for the next page, which is modal. This next page executes a fetch with a predicate, simple, finding all the entities in a to-many relationship.
Now, if I do not save the inserted entities prior to pushing to the next modal page, the fetch is correct. But.. if I save with [self.managedObjectContext MR_saveOnlySelfWithCompletion] and then push, the fetch is sometimes correct, sometimes (most times) random and incorrect. For example there are 5 entities it should be fetching, but it fetches 1, 3, 4, sometimes none. Very odd!
Even more odd is I keep the number of entities as a variable in the modal page, which shows 5 correctly. Always. And if I print out the parent entity of the to-many relationship, it does have all 5 relationships set (and the inverse is set correctly as well).
I have read about fetching the permanent IDS before saving, but that did not make a difference.
Anyone know what is going on?
I guess that your issue might be caused by the fact that you are moving to the next modal page before MR_saveOnlySelfWithCompletion is done.
Try to push the next modal from within the completion block you pass to that method and everything should work.
Ok, I was thinking about the child/parent relationship wrong. I watched the Core Data Best Practices from WWDC 2012, and sorted out what was going on.
I initially thought saving was necessary for the child to be able to access parent changes. Turns out that is not true - saving prior to the segue was unnecessary for the child context to access the changes on the parent context. So removing the save will have to do as it is not needed.
As an aside, I was using existingObjectWithID to get the object from the next modal page, which was unnecessary, as the changes from the parent context would be available to the child. I am not 100% sure why the object would not be fetched by this method as the documentation states If there is a managed object with the given ID already registered in the context, that object is returned directly which seems to me like it should be.
As another aside - saving the context from the child after pushing to modal (before the fetch) works just fine..
Still not really sure why the inconsistency with the fetch after the save from the parent VC.

How to write changes from one ManagedObject to similar ManagedObjects

I am new with Core Data and have a problem which sounds trivial to solve (at least thinking in SQL) but I can't get my head around that with Core Data.
What I'm trying to do is the following: I have a fetched ManagedObject, do some changes and save it again. This ManagedObject has an attribute id. I want to write the changes I made to this ManagedObject to all the ManagedObjects with the same id.
I was thinking to overwrite willSave: and fetching the other ManagedObjects with the same id there but this won't work because I would encounter an infinite loop there.
Can somebody give me a hint on how to progress from here? Thanks in advance
You could make willSave work, but it isn't going to be a nice bit of code to ignore all of the invalid triggers.
It's better to have a class which manages this functionality, pass in the new data value and the attribute id and allow it to do the fetch and update all of the fetched objects (and trigger the save).
I would, indeed, try to find some better way to deal with it, because actually you should't think of Core Data as of SQL with its triggers.
But actually you can indeed use willSave method and avoid infinite loop. See
NSManagedObject Class Reference willSave method
If you change property values using primitive accessors, you avoid the possibility of infinite recursion, but Core Data will not notice the change you make.
So basically in your willSave method you'll need to call some fetchRequest to get all instances of the same Entity, then loop through them and update using primitive accessor: setPrimitiveValue:forKey:
Also I would advice to verify objects in loop whether they are removed (-isDeleted) and, probably, whether that object is not your current one (by comparing managedObjectIDs)

Core data and data integrity: read operations vs write operations. How to protect?

I have what I think is a simple task.
I have a method called [self getPerson] that makes a simple GET request from a web service for a Person that returns some JSON and then transforms the JSON into an NSManagedObject. checks for an existing identical Person NSManagedObject, and if none is found, saves the Person into core data. No problem.
However, If I fire off this method twice in a row, I get two Person NSMangedObjects persisted into Core Data. For example:
[self getPerson];
[self getPerson]; ---> yields duplicate `Person` objects saved in core data, no good.
How can I ensure that only one Person object is saved in Core Data (no duplicates allowed)?
I know the issue, I just don't know how to fix it. The issue is that I need a transaction. When [self getPerson] fires the first time, the method checks for an already existing identical Person object, finds none, and saves a new Person into core data. This is correct. When I fire [self getPerson] the second time, the method checks for an already existing Person object, doesn't see one, and is then persisting another Person object. This is not correct. I would like to make it so that the second time, and third time, and fourth time, to the 1000th time, checking for an existing Person object will only occur after the managedObjectContext saveoperation is done. Right now the check for an existing object is happening so fast (before the save is done).
Do I need a serial queue? If so, should this be a dispatch_async or dispatch_sync? I've even toyed with the idea of trying to use a performSelectorWithDelay trick.
Once you create the object it will exist in the database regardless of you calling save. So you should not create a managed object if one exists already. It's not entirely clear what your code logic is but from your description you say you transform the JSON to a managed object and then you check for an identical existing one and if none is found you save. Well when you create the managed object you have created it, so it's too late to check if an identical one exists. Saving does not create the object it just saves it to the store if it hasn't already been saved.
So first check if an person object exists with the attributes in the JSON and if not then create a managed object.
Well, in this case a serial queue will ensure that operations are performed in the correct manner.
From you question, maybe I'm missing something, I cannot understand if the getPerson method is responsible to both get and save data. If not, you should do it.
Anyway, if you use JSON and the person you retrieve form the server has a unique identifier, you should use that to query against Core Data and verify if it exists or not. The correct manner to do it is to implement Implementing Find-or-Create Efficiently.
A simple question. Is the any reason for calling the getPerson twice? Could you not prevent it using a flag (or a transient property)? Just simple ideas.

Refetching object when traversing relationship in Core Data

In our app, we make use of multiple NSManagedObjectContext instances, one per thread created. So when I'm about to use any object, I always make sure that I got the object from the correct context by doing this.
object = (ObjectClass *)[[contextProvider contextForCurrentThread] objectWithID:[user objectID]];
Since I want to make sure the object is updated I go and refresh it:
[[contextProvider contextForCurrentThread] refreshObject:object mergeChanges:YES];
And my question is: If I want to traverse relationships from this object, i.e object.someRelationship, Should I also make sure that the relationship is in the current context, and refresh it? What would be the correct approach?. Refetching and refreshing every time is such a pain...
Look into nested contexts and concurrency types. They won't solve your underlying problem but they are much better than what you already have.
You need policies. To me it seems that your software (without knowing it at all) is lacking structure. Structure comes with policies.
Even if you were to refresh your objects (all of them) all the time you have serious race conditions.
Without knowing the details of your app nobody will be able to suggest anything except "think about what you do" and "introduce policies".

Resources