ios 8 NSFetchedResultsController with multiple views - ios

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.

Related

UITableView - Delayed update when using sections

I've been searching for a few days, and have continued to learn from reading the apple docs and various tutorials, but there's a problem which I can't seem to get a handle on.
I have a simple app that keeps track of projects, with a single Core Data Entity (ProjectEntry). All the attributes are strings at the moment. It's basically a combination of Paul Hegarty's Core Data lectures and Tim Roadley's web tutorial.
I can add and save Entities and populate a tableview with the data. For the moment, I'm using the CoreDataTableViewController subclass that Hegarty provides in the lecture. I'm mentioning that because...
When my UITableView isn't divided into sections, the new information "instantly" appears in the UI after adding a new entry. However, when I add sections ( via sectionNameKeyPath), the new data still saves, but shows up only after refreshing several times OR waiting 30 seconds before refreshing(via a pull-to-refresh mechanism, which Hegarty also provided).
The tableview delegate methods all seem to be working, as do the NSFetchedResultsController's methods. Using the Stanford/Hegarty CoreDataTableViewController subclass in the past has yielded success, and I've learned a lot reading through the implementation file itself.
The controllers are embedded in a Navigation controller, with the managed object context being passed among the controllers via the prepareForSegue method. Some simple logging shows me the managed object context, initially obtained via a UIManagedDocument, is being successfully passed along.
I've tried the [self.tableview reloadData] and/or the beginUpdates/endUpdates in viewWillAppear, but the delay persists.
What is it about dividing the tableview into sections that's causing the delay? Would calling reloadSections on the tableview be necessary? Like I said, the entries are saved with Core Data and the fetchedResultsController populates the non-sectioned tableview instantly...
It's probably something obvious that I'm just missing, but any help would be appreciated.
Warren Burton's comment above made me re-check if my managed object context behavior was consistent while being passed among the view controllers via the prepareForSegue method.
So, as mentioned in my last comment above, it seems to solve the problem for the moment:
from above:
The initial view controller is non-tableview, which can segue to a UIViewController to add a ProjectEntry, or segue to a UITableViewController which lists saved project entries. This initial view controller creates or uses the UIManagedDocument. By setting a one-line check to see if the UIManagedDoc is being used in this initial view controller's viewWillAppear method, the "delay" in displaying new section data in the tableview seems to stop. I'll test it some more before saying the issue is solved. This way, the shared managed object contexts seem better "bound together" via the managed doc - vapul

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.

Nil out NSFetchedResultsController Delegate when offscreen?

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.

Core Data: Why need the NSFetchedResultsControllerDelegate?

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.

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