Ad

Our DNA is written in Swift
Jump

Radar: NSFetchedResultsController does not get refreshed for added relationship

This was quite a head-scratcher for me today. So I documented it as a Radar (rdar://11541277) because it just feels wrong to me. If there are reasons for this behavior I was unable to find them documented.

Please let me know via Twitter or in the comments if you have a good reason for this or know of any documentation related to this.

Summary:

Adding a relationship after a save does not update NSFetchedResultsController that uses a predicate containing this relationship.

Create Entity -> Save Context -> Add Relationship -> Save Context => does not show object in fetched results controller

Create Entity -> Add Relationship -> Save Context => does show object

Steps to Reproduce:

A NSFetchedResultsController is based on a predicate where in a many-to-many relationship one element in the searches relationship needs to be a certain one. i.e.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@”(isRetweet == NO) AND (%@ IN searches)”, _search];

Create a new entity on a background queue that has the same store coordinator as the main thread MOC so it can write to disk. Save the background MOC which will merge the changes via change notification into the mainMOC. Releationship is not yet set, so the fetched results are empty.

Now set the relationship, this would now qualify the fetched results to contain this new object. But it does not show up.

Leaving the view controller using the fetched results controller and returning has the items show up.

Expected Results:

Adding a relationship should trigger the expected update of the fetched results controller having a predicate to include these objects.

Actual Results:

The update is not triggered if the adding of the relationship is done after the first save of the new managed object. Only setting the relationship and then saving does not trigger anything on the fetched results controller.

Regression:

n/a

Notes:

Obviously the workaround is to have all the changes of the new object occur together so that the setting of other attributes made the object “dirty” and cause the notification. But this is confusing nevertheless.

Update June 25th: Finally got around to create a demo app to demonstrate the issue: relationbug.zip

to see the error occurring:

  1. run the app
  2. on the Timelines VC tap + to add a Timeline entity
  3. tap on the added Timeline entity
  4. first, with the error switch off, tap the plus to add a Message entity
  5. note that the new entity appears, two properties and the relation are part of the same save
  6. turn the error switch to ON
  7. tap on the plus to add another Message entity
  8. Note that you don’t see it appear. You have to exit via back button and return.

The problem occurs if:

  • using a temporary background MOC
  • create a ManagedObject, set properties and save
  • this causes an update notification which is consumed in the demo’s app delegate, but this is not sufficient to update the Messages fetchedResultsController because the Timeline relation is not set yet
  • then the relation is set and another save of the tmp MOC is called
  • the second save, if that’s the only update since the previous save, does not cause the fetched results controller to update
  • although the relationship is persisted, what you can see by exiting and returning (i.e. redoing a full fetch) to the MessagesVC.

Categories: Bug Reports

2 Comments »

  1. After days of searching I finally came across this. It’s the exact same problem I’ve been having and I’m glad to hear I’m not the only one. Is there anyway to just mark the new object ‘dirty’ after setting the relationship. I don’t always have the relationship available so I can’t rely on saving the context after I set the relationship.

  2. Just add a boolean attribute, for example, named “triggerUpdate” to entity TimeLine; when timeLine’s relationship changed, set timeLine.triggerUpdate = @(!timeLine.triggerUpdate.boolValue), done.