Using NSFetchedResultsController outside of a UITableViewController - ios

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.

Related

ios 8 NSFetchedResultsController with multiple views

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.

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.

Why should I use NSFetchedResultsController in a hierarchical ViewController structure?

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.

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.

Resources