Skip to content

Presenters

Diego Sánchez edited this page Jul 4, 2016 · 4 revisions

Presenters

Presenters are mini controllers for your cells. They are responsible for registering cells in the UICollectionView, provide heights for the layout and configure the cells itself.

public protocol ChatItemPresenterProtocol: class {
    static func registerCells(collectionView: UICollectionView)
    var canCalculateHeightInBackground: Bool { get } // Default is false
    func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat
    func dequeueCell(collectionView collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell
    func configureCell(cell: UICollectionViewCell, decorationAttributes: ChatItemDecorationAttributesProtocol?)
    func cellWillBeShown(cell: UICollectionViewCell) // optional
    func cellWasHidden(cell: UICollectionViewCell) // optional
    func shouldShowMenu() -> Bool // optional. Default is false
    func canPerformMenuControllerAction(action: Selector) -> Bool // optional. Default is false
    func performMenuControllerAction(action: Selector) // optional
}

There's a 1-to-1 relationship between a ChatItemProtocol and a Presenter.

A Presenter lives as long as its related ChatItemProtocol is given to the BaseChatViewController by the Data Source. See Update flow to know more about the conditions when a Presenter is reused after an update.

Presenters have to deal with cell reuse, as they will be given different cell instances as the user scrolls in the UICollectionView. However, a Presenter will always present the same message.

There's a convenient BaseChatItemPresenter<CellT: UICollectionViewCell> that keeps track of the visible cell so you can forget about cell reuse.

How Presenters are created

Presenters are the place where your code executes. Therefore, we didn't want to impose any restrictions on their initializers, so we had to add another layer responsible for their creation.

public protocol ChatItemPresenterBuilderProtocol {
    func canHandleChatItem(chatItem: ChatItemProtocol) -> Bool
    func createPresenterWithChatItem(chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol
    var presenterType: ChatItemPresenterProtocol.Type { get }
}

A collection of PresenterBuilders must be given to the BaseChatViewController during the load process so we can register cells in the UICollectionView and create the Presenters to configure the cells.

public typealias ChatItemType = String
public func createPresenterBuilders() -> [ChatItemType: [ChatItemPresenterBuilderProtocol]] {
    assert(false, "Override in subclass")
    return [ChatItemType: [ChatItemPresenterBuilderProtocol]]()
}

Alternatively, you can override

public func createPresenterFactory() -> ChatItemPresenterFactoryProtocol {
    // Default implementation
    return ChatItemPresenterFactory(presenterBuildersByType: self.createPresenterBuilders())
}

And provide your own class to that creates Presenters, getting rid of the builders.

TL;DR

  • 1-to-1 relationship with ChatItemProtocol and same life time.
  • 1-to-many relationship with UICollectionViewCell. Different cells may be given to the presenter as they are created or reused by the UICollectionView
  • Checkout BaseChatItemPresenter. It will hold a reference the visible cell so you can forget about cell reuse
  • Presenters are created by ChatItemPresenterBuilders where you can inject all your dependencies
Clone this wiki locally