In my understanding, NSFetchedResultsController do the job of sync data from managed object context to a table view controller. What's the role of the delegate here? I read the apple documentation and don't get it. Please elaborate the issue concisely and to the point.
Well, the brief answer is that the delegate methods allow you to update the table view as appropriate when an action has been taken on a managed object in the NSFetchedResultsController (NSFRC).
For example, if you delete an object that's in the NSFRC's results then it can update the table by removing the appropriate row.
So...
In controllerWillChangeContent: it is a good idea to tell your table view that you're going to change it (so you should call beginUpdates: on it)
controller:didChangeObject:atIndexPath:forChangeType is called when a managed object changes (added, removed, moved, etc). Update the table view as appropriate (e.g. by calling deleteRowsAtIndexPaths:withRowAnimation: if a managed object was deleted)
controller:didChangeSection:atIndex:forChangeType is similar to the above, but for table view sections.
controllerDidChangeContent is called when the changes are complete. Finish up in this method also call endUpdates: on your table view
Hope this helps somewhat.
Related
I have something like this:
A. UICollectionView1 with FRC1 and FRC_Search_1 for UICollectionView_Search_Results_1
B. UICollectionView1 with FRC2 and FRC_Search_2 for UICollectionView_Search_Results_2
C. UICollectionView1 with FRC3 and FRC_Search_3 for UICollectionView_Search_Results_3
D. UICollectionView1 with FRC4 and FRC_Search_4 for UICollectionView_Search_Results_4
.
.
.
Idea was to use NSFetchedResultsControllerDelegate for updating Collection Views. But if I implement it at both A and B, after one update, both implementation will be called. I don't understand why is this happening. Is it because both FRCs are pulling from same entity or because they have same context or something else?
What is the best approach to solve this?
EDIT:
#Tuslareb
I have 2 UICollectionViewControllers with seperate classes. Each has it's own FRC, collectionView and Delegate implementation. If FRCs looks the same ( same entity,predicate...) both delegates are activated when I update only one collectionView in one collectionViewController.
Problem occurs if those 2 collectionViews are different. Then indexPaths are different, and updating produces errors and stops app.
This is probably the way it works, core data is observing type of FRC but not objects itself. But I wanted to be sure.
When a delegate is set for a FRC, the FRC will receive change notifications from the context. Then, the FRC will notify the delegate whose methods will update the collection (or table) view. So, as you already did find out yourself, any changes in the context will notify the FRC delegate when the FRC object is 'alive'.
This answers at least one part of your question. To solve it, you will need to obtain a reference to the collection view that was changed and change the delegate methods in a way that they will only be executed for the view that was changed.
Is it best practice to Nil out NSFetchedResultsController Delegate when offscreen?
For example, I have a list managed by a NSFRC. When I tap a list item, I get a detail view. I can potentially change something on a detail view that will remove it from the list. Or, if I continue to slide through the detail views, I can cause it to load in more data (which would subsequently update the parent table view NSFRC).
I'm getting strange behavior when I DO nil it out. Can't seem to find advice on this anywhere.
No reason to nil out your NSFRC here. In fact, this is the main convenience of having your tableView managed by an NSFRC, is that it will update itself appropriately while you are off in other views changing data. This also applies data changes sequentially, rather than having to refetch all data when returning to your list.
I have posted a similar question time ago, looking for a technical explanation of this 'best practice'. However I didn't find anything.
I suppose that much of the suggested 'best practices' came from older iOS version, where viewDidUnload could have been called in case of memory warning, but that's not the case anymore.
In my experience, I found out that it is only mandatory to resign in case of background update and merge. All the other case depends on your application logic.
For example when you have nested CoreData entities with subsequent UIView, like so:
child1 (UITableView)-->child2 (Detail)-->child3 --> (UITableView)-->ecc...
Then, a change on child3 will have the child 1 table view react on that change, so you may want to avoid unnecessary call to a remote fetched controller, by resigning as delegate or introspecting the changement and returning YES or NO within the delegate method. It could be simpler and much efficient to refetch the query.
Keeping the same structure, suppose you have a mass update on latest UITableView by merging two context, if you have used the same context for all the controllers, then they will be trying to update its view even if not needed.
So for a simple case like UITableView-->UIView (detail) I can see no problem of keeping delegate assigned, all the other case, I would do a little analysis.
I'm making an app where when you select a cell, you're segued to a new view for reading. The cell you tapped on corresponds to an object in the Core Data store (through NSFetchedResultsController) and that object is set as the value for the article property of the view that is being segued to.
Once there, I move the position of the user in the article as they read it. (So I alter the position attribute of article via article.position = ...)
However, this occurs very frequently as they read, often hundreds of times, and each time NSFetchedResultsController is detecting the change as an update, then calling configureCell:, which then runs through a bunch of configuration for that cell. As this configureCell: method is called so often (and I only want it to be called when they go back to the table view, as that's the only time the update is needed) it's causing a decent performance loss.
I don't get why it's calling it though. I'm not saving the data with NSManagedObjectContext into the Core Data store, so why does it care? I only call that when viewWillDisappear is called, indicating that they're leaving the view, likely to go back to the table view which is where I want it!
Basically, how do I get it to only call configureCell: when it needs to/when I ask it to write the data to Core Data? It's calling it hundreds of times as is.
It doesn't matter if you're not persisting the changes to Core Data objects to disk; a change to a Core Data managed object IS a change, and so NSFetchedResultsController acknowledges it.
You could avoid updating the tableview that's not visible by setting NSFetchedResultsController's delegate to nil in viewWillDisappear:, and setting it back to self in viewDidAppear:. Also, add a performFetch: after setting the delegate.
The "hidden" NSFetchedResultsController will still be receiving all the changes made to the article object in the view that's in the foreground, but will ignore them since it doesn't have a delegate.
When going back to the articles list view, it will have a delegate again, and it will be able to react to all changes.
I've a UITableViewDataSource which maintains sections of data items which will be presented by an UITableView instance. The table view is editable, allowing insertion and deletion of rows and sections, and all changes on the view should be written back to the data source. After reading through the Apple documents, I can deal with insertion and deletion on rows by sending message tableView:commitEditingStyle:forRowAtIndexPath: to the data source.
But, however, I can't figure out what is the standard way to feedback the changes on sections to the data source. Please kindly help.
The delegate method tableView:commitEditingStyle:forRowAtIndexPath: is called by the table view to tell you what the user has done (what they have added / removed). It is then the responsibility of your code to make the appropriate changes to your Model and reload the table view.
The simplest way to reload is to call reloadData, and you also have more specific options for reloading / inserting / removing individual rows or sections - this is all from a UI perspective and the table view requires that you have updated your Model before you call any of these methods or the table view will throw an exception.
I have a UITableViewController subclass and when I try to fetch some entities from viewDidLoad, I get an empty array. But when I use the same code in viewDidAppear I get those entities! How's that?
Are you using a NSFetchedResultsController? That is the recommended (and easiest) way to populate a table view with Core Data. You would not have to care about the time of the fetch because the results controller would managed that for you.
Without the FRC you are responsible for populating the table. Depending on your setup, i.e. how you create the view controller, it is possible that all necessary parts (including the managed object context, or a data array, or your table view itself) are not fully loaded yet by the time viewDidLoad runs. Typically, viewDidLoad is best used for view controllers that are loaded from a nib or storyboard. You provide the necessary properties in prepareForSegue: or some such method, and they will be available to viewDidLoad:.
Still, you should go with the FRC. It will do the fetch more or less lazily through the datasource methods. That would also be the best option for performance and memory management.