Why should I use NSFetchedResultsController in a hierarchical ViewController structure? - ios

I searched a bit but couldn't find the answer I was actually looking for. Simple scenario:
TableViewController with an NSArray of beers. If I select the cell, a detail-view with beer details should be displayed. Now the beers are stored using CoreData.
In the RootViewController I use NSFetchedResultsController to fetch all the beers.
Now my question is: Should I just set the beer property of the destination to the selected beer, or should I create a new NSFetchedResultsController and perform a whole new fetch with a set up NSPredicate?
Where is the difference?

Hmm...
For me I think the biggest factor would be whether the data changes or not.
NSFetchedResultsController is good for performing fetches etc... but it comes into its own when dealing with CoreData entities that change.
The NSFetchedResultsControllerDelegate methods are there to update the table/collection view when the CoreData model is updated on a background thread (i.e. from a network request etc...).
If your model doesn't change at all then I'd go with just passing the beer object in and using the properties of that object.
If the model does change then use the NSFRC and its delegate methods to perform a new fetch.

If you already hold the selected object in your RootViewController and it has all attributes that you will present in the detailViewController, then I see no point setting up the NSFetchedResultController for the detailVC. You can just pass the object to detail in prepareForSegue or so.
The point of using NSFetchedController is that you don't have to update your tableview datasource array and reload rows/table with each change in your list provided you use Apple's CoreDataTableViewController template.

Related

How to "sync" UI with Core Data?

I would like to know what is the usual approach for syncing UI with Core Data, for example when you display data stored by Core Data in a table view, and you want to show additional info about an object when the user taps its cell in the table view, how would you link the table view cell to a specific object in Core Data ? I have seen many people not recommending to use IDs since Core Data wasn't designed in this way, but then what to use if we are not using an ID ? I would have wanted to do something like : "OK, the selected cell has this ID, let's go in Core Data fetch the object with the same ID" But if I'm not doing this, how can I do ?
Same question for other cases where you need this kind of link... in my case, I need a link between pins on a map and Core Data objects, so that I display additional info when the pin is tapped, a little bit like in Apple Maps for example...
What should I do ?
Thank you.
You can do that by using NSFetchedResultsController - you can subclass your UITableViewController from CoreDataTableViewController (.h .m) and the only method you need to override would be cellForRowAtIndexPath:. Then can access objects directly like this:
MyCustomObject *obj = [self.fetchedResultsController objectAtIndexPath:indexPath];
For your second question, when you are not using a table view there are two options. One you can fetch the object (assuming you are at the very top of the stack) or two, far more common, you have the NSManagedObject instance passed into you using simple dependency injection during the storyboard segue.
Any view that is below/after a table view should never need to fetch or call something like -objectWithID: as the object is already in memory and the controller that is giving you the ID already has the object. Just pass the object around like any other language.

How to have 2 tableview in one view controller with 1 tableview being dependent to the other

I'm in the process of creating an app that needs to have 2 TableViews that is linked to core data. The first table View is the Parent tableview wherein the user needs to select it before the 2nd table view populates with the associated data. I've been looking at examples and solutions but I came upon an issue with the FetchRequest Controller. how will it know which table to query?
I saw an example here but its not that complete. or at least, It did not gave a complete detail as to how to set the 2nd tableview's Delegate and Datasource. well, the delegate is the ViewController but what about the Datasource? how would it know which data to pull from?
I hope you guys can assist with this dilemma or at least, point me to an example.
I would use a UIViewController rather than a UITableViewController. Add two UITableViews, either in code or in a storyboard, and create two properties for them - let's say parentTableView and childTableView. Make your view controller both the delegate and dataSource for both tableViews. When you implement the relevant methods, begin by checking the tableView parameter that is passed in the method call, and provide the relevant data:
if (tableView == self.parentTableView) {
// provide data relevant to the parent...
} else { // provide data relevant to the child...
}
To provide the data for the tables, you could use one fetched result controller for the parent, and then use the parent - child relationship to derive the data for the child table view (eg. for numberOfRowsInSection for the child, you might use [self.selectedParent.children count], and so on). This will work fine, but you lose the benefits of using a fetched results controller for the child objects: automatic table sections, and automatic updates when new child objects are inserted/updated/deleted.
If you would prefer, you can use two separate fetched result controllers - one for the parent objects, and another for the child objects. Create a property for each, say parentFRC and childFRC, and then use the relevant one to provide the data in the tableView delegate methods as above. For the childFRC, you should set a predicate which limits the fetched child objects to those whose parent is the selected parent in the parentTableView. (You will need to reperform the fetch for the childFRC when the selected parent changes).
If you use the NSFetchedResultController delegate methods to keep track of changes to the data, you should again set your view controller as the delegate for both FRCs, and in your implementation of the relevant methods, check the fetchedResultController parameter that is passed in the method call to determine which object has been changed - parent or child - and update the appropriate table.

Best way to implement UICollectionViewDataSource protocol?

I have theoretical question.
Currently my app is using UICollectionView as a way to display objects list. UIViewController, that contains UICollectionView as subview, implements UICollectionViewDelegate protocol and acts as delegate and datasource. Datasource uses NSFetchedResultsController to provide data;
In my opinion this is not the best way to implement datasource, and implementing it in separate class looks way better idea. But the issue it that datasource depends on search parameters in UITextField, and some other buttons selections, so every time when user types text into search field or press the any of "sorting" buttons I should update datasource (in particular fetchRequest in NSFetchedResultsController).
So, finally, my question: Is there any "best practices" of implementing datasources that depends on external parameters? Should I create separate class for datasource of leave it the way it is now? If implementing datasource as separate class - should I create datasourcedelegate for calling self-made delegate methods on delegate when datasource was updated or there is some other workarounds for this problem (I'm not considering using notifications on datasource update because as for me notifications mechanism is more global solution then I need here)?
I'm not looking for the fastest way, I just want to find out the rightest theoretical way of implementation.
Thank you all in advance :)
I personally implemented a concrete NSObject derived class, that implements UICollectionViewDataSource as well as NSFetchedResultsControllerDelegate that practically translates the fetched results controller events (object inserted, updated, deleted) to collection view events (insert, update or delete cells). You can find examples on how to do this, I took mine from here but I implemented it as a separate class instead of a category over collection view. I found my class highly reusable, in practice I use it in all of my projects where there is a need to visualize managed objects in a collection view. A similar class can be implemented also for UITableViewDataSource.
If you need to update the fetch request with the search predicate, I would subclass your newly created DataSource class, and add the logic to update the fetch request right there. Say, you add a -(void)updateSearchFilterWithText:(NSString*)text method where you add the logic to update the fetch request of the fetched results controller. Don't forget to perform fetch again afterwards and call a reloadData on the collection view!
With this architecture the view controller owns this dataSource object. Every time the user updates one of your filtering text field (or other widget), the view controller calls the updateSearchFilterWithText: of your data source object and the rest of the work is done by this later.
What you currently have is the standard approach. While there is no defined 'best' approach, what you describe is certainly a better approach.
Your view controller would own an instance of your new data source class, and would itself most likely handle the delegate methods (because these are actions to take rather than data to provide), so when anything changes in the UI the view controller should be 'pushing' these changes to the data source. No additional delegation should be required.
You shouldn't be creating your data source with the idea that text fields and buttons are directly driving changes in. Your data source should be presenting a generic interface where you can update the fetch request to execute (which covers the predicate and sorting) and change how the cell is configured (perhaps with a block). This way you keep your business logic in the view controller and the reusable data source code in another class that is reusable for other collection views / projects.

Why I can access Core Data entities in viewDidAppear, but not in viewDidLoad?

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.

Using NSFetchedResultsController outside of a UITableViewController

Would it be wrong to use NSFetchedResultsController outside of a UITableViewController, since non-UITableView controllers would be unable to implement the NSFetchedResultsControllerDelegate protocol?
There is no particular reason you can't use a NSFetchedResults controller without a table. You can define any arbitrary class to implement the NSFetchedResultsControllerDelegate protocol.
However, there is seldom any particular reason why you would want to do so. The FRC is designed to fetch and order data for a table. It tracks section names and the like as well. Other controls don't really need that ordering or that information.
If you want a controller to watch the managedObject context for changes like an FRC, then you should register the controller for context's various notifications. Then the controller can update the view as needed just as an FRC does for a tableview.

Resources