Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to separate dataSource and delegate from viewController class in Swift?

Tags:

ios

swift

I'm trying to separate dataSource and delegate from viewController to prevent the viewController from being messy. I read through some posts and found I can separate dataSource like below, create a class to represent dataSource:

import UIKit

class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {

    var movies = [String]()

    //MARK: - UITableViewDataSource
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return movies.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as UITableViewCell


        cell.textLabel?.text =  movies[indexPath.row]

        return cell
    }

}

My question is: What should I do if I want to use properties or call methods of viewController class in DataSource class? For example, I want to call presentViewController when the user select a cell:

func tableView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    //do something here

    presentViewController(viewController!, animated: true, completion: nil)
}
like image 755
ilovecomputer Avatar asked May 06 '16 08:05

ilovecomputer


2 Answers

You can handle a selected callback:

class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {

        var movies = [String]()
        private var selectedCallback = ((NSIndexPath)->Void)?

        func selectedItemAtIndex(callback:(NSIndexPath) -> Void) {
            selectedCallback = callback
        }
}

callback:

func tableView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    //do something here

    if let callback = selectedCallback {
       callback(indexPath)
    }
}

Using:

dataSource.selectedItemAtIndex() {
  [weak self] indexPath in
  // do something ...
  // presentViewController(viewController!, animated: true, completion: nil)
}
like image 124
tuledev Avatar answered Oct 18 '22 17:10

tuledev


You don't want to couple your datasource with navigation logic like presentViewController. Instead, your datasource can have a delegate, and the datasource will tell the delegate that a certain action was performed by the user. This way, the delegate does not need to know about details like indexPaths; the delegate of the datasource can be the viewController that owns both the collectionView and the dataSource of the collectionView.

UIViewController: has properties UICollectionView and a property YourDataSource. And the viewController sets itself as delegate of YourDataSource instance. In the implementation of YourDataSource's collectionViewDidSelectItemAtIndexPath, you can retrieve the movie that lives at that indexPath, and call the delegate with a method like

func  dataSource(dataSource: YourDataSource, didSelectMovie movie: YourMovieClass)

This will nicely make the viewController unaware of how this movie was selected, and can just a create, setup and present a new viewController for this movie.

like image 30
Joride Avatar answered Oct 18 '22 15:10

Joride