Best way to implement UICollectionViewDataSource protocol? - ios

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.

Related

Working principle of delegate and datasource

Just want to know the working priciple of iOS delegate and datasource.
For example, when I call a [tableView numberOfRowsInSection] in some ViewController.
Seems that it is returning me [tableView.dataSource numberOfRowsInSection]
But how can I invoke [tableView numberOfRowsInSection] without returning [tableView.dataSource numberOfRowsInSection]?
May I know what is happening behind the code? Thank in advance.
In iOS, delegate and dataource are implemented as delegation pattern. The different between them is about it's responsible and relationship with the delegating object. delegate is control of user interface, while data source is control of data. They all have to adopt protocol, in which defines a set of methods that act relevantly with each others.
Table View delegate and data source implemented aim to adopt one of best practice of using delegation pattern. Delegation design pattern responds sometimes as a callback function, however, in the case of table view data source and delegate, it allows you to control and customize properties and behavior of table view.
An example of that you can see is that implementing delegate and data source allows you to customize your table view properties such as number or sections, number of rows, which type of cells and it's behavior like clicks a row.

What is best practice when it comes to reusable UITableViewCell's and delegates?

I'm building an app that presents table views in a majority of the screens. They present a handful of different table view cells, but there is one that is presented in 3/5 of the table views. This cell, which displays a video and provides an interface for users to interact with the video (like, comment, flag, delete, play/pause, etc), has a fairly large delegate with seven methods/functions.
My question is:
Would it be a best practice to set up a separate controller that would be a property of my view controller to be assigned as the delegate to the cell, or to subclass a UITableViewController with the methods already implemented?
The problems I see with the latter is that I would then have to implement a weird way to handle the data source (set up methods to return the models, always ensure that videos are stored in that array) and the former just seems a little odd to the standard MVC practice. Any advice?
Update
I began factoring out a data source to use that implements the cell's protocol. Another issue that I seem to be running into is displaying multiple cells, i.e.:
I have a searchDisplayController that displays UserCell's and VideoCell's, based on the selectedScopeIndex of the search bar. One way I could handle this is to create a dataSource for this tableView that handles both cases, or swap out data sources based on changes to the selectedScopeIndex. Are either of the two options looked down upon? Is swapping a table view's data source valid?
I solved this issue by implementing a UITableViewDataSource controller that would also handle the cell's delegates. I was able to shorten the 7 method delegate to a 3 method delegate on the data source, used to push new controllers, remove objects from the data model, and handle fading/updates.
Granted, I needed to pass reference to the UITableView, the UIView, and the UIStoryboard of the source UIViewController, but the code is much more readable and manageable.

Displaying complex data in a UITableViewCell

I have a custom UITableViewCell class that I use to display quite a complex set of data.
Essentially the cell displays a Match object. But in doing so it displays information about the two Teams, the score, the time elapsed and so on.
Thinking about MVC and clean code.
Should I just pass in the Match object and let the cell do everything? Or is it better practise to expose the different elements of the cell (team1NameLabel, team1ScoreLabel, team2NameLabel, etc...) and set them all individually in the UITableViewController?
The first way makes the UITableViewController cleaner but then I'm relying on the UITableViewCell to "know" about the Match class, the Team class etc...
The second way makes more work for the UITableViewController but then makes the UITableViewCell a "dumb" display. All it does is then lay out the information within the cell. It doesn't know anything about the information it is displaying.
I would follow these rules:
The cell should just have the outlets for displaying the various bits of data. It is a view so it should not contain any logic.
The controller should get the Match data, parse and make calculations if necessary, and populate the cell. It is a controller, so that is its primary function in a MVC context.
IMO it is better and more MVC-like to pass the Match object to your table view cell.
Lot of the code you find on the internet(even Apple examples if I remember well) is not doing that. You can see many times a configureCell method within the view controller that is called in tableView:cellForRowAtIndexPath:.
I prefer to pass the model object object to the cell, it makes my view controller code simpler and also it is simpler to unit test: when I test my view controller I only verify that the model object is passed to the cell, and then in the table view cell tests I verify that the test of the labels is set to the expected values. Someone may say that this is making the view knowing about the model, but I don't see any big problem on that.
Both ways are fine, but personally I would go for second option, i.e. table view exposing #property and, if necessary, outlets.
However, if you really want to go for the first option, I would suggest to have any objects passed to the cell to implements a protocol exposing few methods:
#protocol tableViewCellProtocol
-(NSString*)titleForCell;
-(NSString*)descriptionForCell;
Then you can "pass the protocol" rather than the object.
[mytableCell renderObject:objectImplementingProtocol];
This way you slightly decouple the objects itself, and prepare cell for reuse with other objects.

Is this the correct "layout" for delegation?

I'm building a simple calendar application based in a single view controller. The view controller has a hierarchy as follows:
CDViewController
CalendarView
EventView
AgendaView
TPKeyboardAvoidingTableView
EventInfoCell
My question specifically pertains to the EventInfoCell within my AgendaView. The Cell has various ways of laying out textFields and textViews, depending on the information that needs to be displayed. These textfields and textviews are enabled/editable based on the tableview's edit status and edit/delete core data objects. When the strings within these change, the cell needs to alert the managedObjectContext to save or delete an object, update the model within the agendaView that populates it's tableView, and update the model that pertains to all events for the calendar. My current configuration makes the viewController the delegate of EventInfoCell, but this complicates the update for the agendaView model. It seems almost counterproductive to set the EventInfoCell delegate to be the AgendaView, tell it when information changes, and then have the AgendaView pass the word on to it's delegate (the viewController), but if it is more compliant to MVC than bypassing the agendaView altogether, I will gladly implement it. I'm trying to figure out what is the better design pattern, any input is greatly appreciated.
"It seems almost counterproductive to set the EventInfoCell delegate to be the AgendaView, tell it when information changes, and then have the AgendaView pass the word on to it's delegate"
Why does this seem counterproductive? This is the way I would handle it. If you don't do it this way then you are going to have to open up some sort of properties or a notification or something in the AgendaView for the ViewController to pass the information back to it. That sounds more counterproductive to me than passing the information up the chain. Plus, you may eventually have some sort of changes in the EventInfoCell that require its delegate to act, but do not necessarily alter the Core Data model. So, I think you should pass the delegation up the chain.

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