Why does my NSFetchedResultsController delegate stop firing when I add an NSPredicate? - ios

I have an NSFetchedResultsController displaying "places" in a table view, but when I update the set of places that should be displayed in another view controller, my FRC does not update the table view.
That's the general problem. My specific case seems to revolve around the NSPredicate backing my FRC, because when I remove the NSPredicate (and just get all places), everything works fine.
My query is
#"ANY photos.isFavorite == %#", [NSNumber numberWithBool:YES]
places have a one-to-many relationship with photos (I am working through CS193P). Perhaps my FRC is not set up to observe changes in a related table or something?
A bit of additional information about my situation:
My Core Data updates and queries seem okay, as my "places" table is always correct when I first load the application.
My FRC does update rows that are already present at application load. It just won't insert new rows/sections at runtime.
I am only using a single MOC.
My sectionNameKeyPath is not set to a transient attribute.
My cacheName is set to nil.

I don't know if it's your case but I'll post anyway. Maybe it could be a valid workaround.
NSFetchedResultsController pitfall
Hope it helps.

Did you try testing a simpler predicate, for example using a photo one-to-one relationship instead of photos?
"my FRC does not update the table view" - Are you using -controllerDidChangeContent: ? Did you verify it is not getting called? I've seen that contrary to the documentation, calling reloadData on the tableView from within this method is unsafe, since the method may be called on a secondary rather than the main thread.
Are you performing all operations related to the MOC on the same (main?) thread?

Try this predicate instead?
#"%# IN photos.isFavorite", [NSNumber numberWithBool:YES]

Related

How can I make NSFetchedResultsController event correctly when predicate values change?

In my Core Dara model, there is a single Session object that holds a single Order object (though other orders can be floating around in CoreData) and Order holds Purchases. I've got an NSFetchedResultsController that fetches Purchases for the Order in the Session.
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"order.session = %#" argumentArray:#[session];
This works fine for fetching, but it doesn't call back the delegate methods for the fetched results controller in the case that the Order becomes detached from the Session. Is this just a failure in the NSFetchedResultsController, or is there a documented limitation? More importantly, how can I get around that limitation in a clean way?
To be clear, the results controller always returns the correct result after calling performFetch: it's just not firing the delegate methods.
I think this is just a "feature". The fetched results controller tracks changes to its fetched objects, but as far as it's concerned those objects are unchanged - the only change is to the related Order object. I think your best bet would be to use Key Value Observing of your Order object (or Session object) in order to be notified if they become "disconnected".

unattached (disconnect) Core Data entity from context

I'm using magical record for all my core data work.
everything works great, with the exception at times when I'm doing updates in the background I need to detach or disconnect the entity from the context.
for example
ButtonList = [Buttons MR_findAllSortedBy:#"listOrder" ascending:YES];
How would I keep the entity, but remove the reference to the context for the array ButtonList?
Thanks
This will only happen when you don't use a NSFetchedResultsController, or code that observe context changes and remove deleted objects from the UI to reflect the store state.
If you like the deleted objects to be removed from view as soon as your context finds out about the deletion, you would need to listen for "context did change notification" on your main context and look at the deleted objects set, if any of the deleted objects are part of your display array you will need to update your view accordingly (remove from array and update table. a NSFetchedResultsController also listen for context changes).
Another option:
Since you manage your tableview state by yourself (and not a fetched results controller) and
If you like the "buttons" to remain in view including their properties, you could:
Change your request to return dictionaries instead of managed objects (does not nullify on deletion):
NSFetchRequest* r = [Buttons MR_requestAllSortedBy:#"listOrder" ascending:YES];
[r setResultType:NSDictionaryResultType];
//This is your link to the data store and managed object (if you later need to fetch by or update if still exist)
NSExpressionDescription* objectIdDesc = [[NSExpressionDescription new] autorelease];
objectIdDesc.name = #"objectID";
objectIdDesc.expression = [NSExpression expressionForEvaluatedObject];
objectIdDesc.expressionResultType = NSObjectIDAttributeType;
[r setPropertiesToFetch:#[objectIdDesc,#"buttonName",#"buttonIcon"/*, and any other property you need for display*/]];
Now all is left to do is execute this request on any context you like (even in background) and get the array back to your table view controller.
The difference here is you get back dictionaries and not NSManagedObject array.
In Core Data there's no such concept of "detaching" an entity from a given context. You would have tons of problems in attempting to do something like that (e.g. move objects between contexts/persistent stores), especially when dealing with relationships.
I think you should refactor and design your application in a way that reacts proactively to any changes in the object(s) you are representing, e.g by removing the related table view cells (with NSFetchedResultsControllerDelegate callbacks), by automatically popping the detail view controller if present, etc...
I would not recommended recommend it but, if there are states for the object you're representing which are "transitory" and shouldn't be reflected in the UI, you could temporarily nil the NSFetchedResultsController delegate (I assume you're using that to display your managed objects) so that it doesn't receive updates and doesn't update the table view (for example). When the objects are ready to be displayed again, you can set the delegate back and perform a new fetch, so that the tableView gets updated (with automatic cell insertions, removals and updates).
If an object has been fetched from a managed object context, the only way to break its connection to the context is to delete the object and then save changes. There is no way to convert a fetched object into something that's not associated with its context. You could copy the data to some other object, but the one that you fetched is and will always be associated with the context that you got it from.
In your case, if you're deleting these objects in a background queue, you should not be using them any more. Or if you do need to use them, then you should not be deleting them. I can't tell from your question just what you're trying to accomplish here, but what you've described makes no sense.

Core Data Conundrum: Cannot call NSFetchedResults twice

I have a custom UITableViewController, called mainViewController.
It has a public property in the header file
#property (strong, nonatomic) NSMutableArray *someGroups;
This property is set in the viewDidLoad method
_someGroups = [[GGGroups findAllSortedBy:#"lastUpdated" ascending:NO withPredicate:[NSPredicate predicateWithFormat:#"ANY users = %#", [GGUser currentUser]]] mutableCopy];
However, for some reason, if I ever call the following, once the view has loaded, it returns 0 objects.
[[GGGroups findAllSortedBy:#"lastUpdated" ascending:NO withPredicate:[NSPredicate predicateWithFormat:#"ANY users = %#", [GGUser currentUser]]] mutableCopy];
This is quite annoying especially when I want to reloadData in the table view controller, since this array is the data source, so the table view becomes blank since this method returns 0 objects now.
I am currently using MagicalRecord, does anyone know why this occurs.
Thank you!
There is nothing in MagicalRecord that caches your find* requests. Every call to any of the find methods will generate a new request and perform a new fetch on that new request. My guesses as to why your second fetch is failing:
The default context (which you are implicitly using by not specifying one) is nil somehow
Your currentUser method is returning nil
Your data store is empty on that second call
You aren't actually calling your method to reload data at the right time
You are not refreshing your view of data
MagicalRecord has logging built in. I suggest turning it on and seeing if there are any errors that show up in the logs. Those will help pinpoint any issues that might be arising with Core Data.

iOS: How do I add/remove a NSPredicate to my FetchedResultsController from a button press?

I have a button that I currently have set up as a toggle. It currently only changes it's text when pressed. I am wanting to have it filter out entries in my uitableview though. I'm guessing I'd do that by adding a NSPredicate to my fetchedresultscontroller, but I'm unsure how to do this. Anyone know how I might be able to do this or send me in the correct direction if this is not the proper way of doing this task? Thanks!
On button click, you would be require to release the instance of fetchedresultscontroller (if in memory), as you would be require to re-create FetchRequest with Predicate in it.
After that, you could call execute api. As, fetchedresultscontroller will be required a new predicate.
Preferrable is to use 1 level of fetchedResultsController api call from Core Data. Because Fetching from DB is costly as compared to filtering the objects w.r.t already loaded objects in memory.
In your case, use fetchedresultscontroller with fetching all required apis, and as per toggle, just use predicate on in-memory array/records. This will improve performance and secondly, since the records will be core-data managed objects, so if they will be in no-more use, they could get faulted.

Post-fetch sorting / Workaround for NSSortDescriptor + NSFetchRequest / Sorting a tableView

My quest to collect the scattered, magical pieces of code needed to create an animated and dynamically updating UITableView - artifacts hidden in the dark and ghastly depths of Apple Inc's feared dungeon, The Documentation - has with the help of the ever-friendly townsfolk and everyday heroes of Stack Overflow finally been completed.
But worry not, the end of this quest is but the beginning of a new one.
I have one UITableView. That tableView is hooked up to a NSFetchedResultsController. The FRC delegate methods are all up and running as pr. Apple's example code.
I have two NSManagedObjectContexts:
The Truth. This MOC is only inserted to / deleted from when the user adds or deletes an object.
The scratch pad. This is the MOC that the tableView's FRC is hooked up to. Any change here is reflected in the tableView with nice animations.
The scratch pad is seeded with objects from The Truth, but it is never saved. This means that I can insert and delete (show and hide) objects here to my heart's content, all while the tableView politely updates.
(To anyone reading this in an attempt to implement something like this I would say: get acquainted with [managedObjectContext objectWithID:id])
My question comes from the need to sort the tableView. As made clear to me by reading about NSSortDescriptor in conjunction with NSFetchRequest, using a sortDescriptor simply won't fly when one's using a SQLite store. The Docs say, "instead you should sort the returned array in memory". All-right then! But how do I go about doing that?
Where, in my logic as described above, do I inject this sorted array? Wherever I turn, there seems to be problems.

Resources