Skip to content

Commit

Permalink
BaseChatViewController calls update for reused presenters
Browse files Browse the repository at this point in the history
  • Loading branch information
MikhailGasanov committed Aug 1, 2019
1 parent e4de8db commit cdbc86b
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 12 deletions.
9 changes: 9 additions & 0 deletions Chatto/Source/Chat Items/BaseChatItemPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ open class BaseChatItemPresenter<CellT: UICollectionViewCell>: ChatItemPresenter

public init() {}

open var isItemUpdateSupported: Bool {
assertionFailure("Implement in subclass")
return false
}

open func update(with chatItem: ChatItemProtocol) {
assertionFailure("Implement in subclass")
}

open class func registerCells(_ collectionView: UICollectionView) {
assert(false, "Implement in subclass")
}
Expand Down
4 changes: 4 additions & 0 deletions Chatto/Source/Chat Items/ChatItemProtocolDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public protocol ChatItemMenuPresenterProtocol {

public protocol ChatItemPresenterProtocol: AnyObject, ChatItemMenuPresenterProtocol {
static func registerCells(_ collectionView: UICollectionView)

var isItemUpdateSupported: Bool { get }
func update(with chatItem: ChatItemProtocol)

var canCalculateHeightInBackground: Bool { get } // Default is false
func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat
func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell
Expand Down
10 changes: 9 additions & 1 deletion Chatto/Source/Chat Items/DummyChatItemPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@

import Foundation

// Handles messages that aren't supported so they appear as invisible
// Handles messages which aren't supported. So, they appear as invisible.
class DummyChatItemPresenter: ChatItemPresenterProtocol {

class func registerCells(_ collectionView: UICollectionView) {
collectionView.register(DummyCollectionViewCell.self, forCellWithReuseIdentifier: "cell-id-unhandled-message")
}

var isItemUpdateSupported: Bool {
return true
}

func update(with chatItem: ChatItemProtocol) {
// Does nothing
}

var canCalculateHeightInBackground: Bool {
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@ extension BaseChatViewController {
return self.createPresenterForChatItem(decoratedChatItem.chatItem)
}

guard oldChatItemCompanion.presenter.isItemUpdateSupported else {
return self.createPresenterForChatItem(decoratedChatItem.chatItem)
}

oldChatItemCompanion.presenter.update(with: decoratedChatItem.chatItem)
return oldChatItemCompanion.presenter
}()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,16 @@ class FakeDataSource: ChatDataSourceProtocol {
class FakeCell: UICollectionViewCell {}

class FakePresenterBuilder: ChatItemPresenterBuilderProtocol {
var presentersCreatedCount: Int = 0
private(set) var createdPresenters: [ChatItemPresenterProtocol] = []

func canHandleChatItem(_ chatItem: ChatItemProtocol) -> Bool {
return chatItem.type == "fake-type"
}

func createPresenterWithChatItem(_ chatItem: ChatItemProtocol) -> ChatItemPresenterProtocol {
self.presentersCreatedCount += 1
return FakePresenter()
let presenter = FakePresenter()
self.createdPresenters.append(presenter)
return presenter
}

var presenterType: ChatItemPresenterProtocol.Type {
Expand All @@ -99,6 +101,20 @@ class FakePresenterBuilder: ChatItemPresenterBuilderProtocol {
}

class FakePresenter: BaseChatItemPresenter<FakeCell> {

var _isItemUpdateSupportedReturnValue: Bool = false
override var isItemUpdateSupported: Bool {
return self._isItemUpdateSupportedReturnValue
}

private var _updateWithChatItemCalls: [(ChatItemProtocol)] = []
var _updateWithChatItemIsCalled: Bool { return self._updateWithChatItemCallsCount > 0 }
var _updateWithChatItemCallsCount: Int { return self._updateWithChatItemCalls.count }
var _updateWithChatItemLastCallParams: ChatItemProtocol? { return self._updateWithChatItemCalls.last }
override func update(with chatItem: ChatItemProtocol) {
self._updateWithChatItemCalls.append((chatItem))
}

override class func registerCells(_ collectionView: UICollectionView) {
collectionView.register(FakeCell.self, forCellWithReuseIdentifier: "fake-cell")
}
Expand Down Expand Up @@ -131,6 +147,8 @@ final class FakeChatItem: ChatItemProtocol {

final class FakeChatItemPresenter: ChatItemPresenterProtocol {
init() {}
var isItemUpdateSupported: Bool { return false }
func update(with chatItem: ChatItemProtocol) {}
static func registerCells(_ collectionView: UICollectionView) {}
func heightForCell(maximumWidth width: CGFloat, decorationAttributes: ChatItemDecorationAttributesProtocol?) -> CGFloat { return 0 }
func dequeueCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell { return UICollectionViewCell() }
Expand Down
47 changes: 39 additions & 8 deletions Chatto/Tests/ChatController/BaseChatViewControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class ChatViewControllerTests: XCTestCase {
fakeDataSource.chatItems = createFakeChatItems(count: 2)
controller.chatDataSource = fakeDataSource
self.fakeDidAppearAndLayout(controller: controller)
XCTAssertEqual(2, presenterBuilder.presentersCreatedCount)
XCTAssertEqual(2, presenterBuilder.createdPresenters.count)
}

func testThat_WhenDataSourceChanges_ThenCollectionViewUpdatesAsynchronously() {
Expand Down Expand Up @@ -285,7 +285,8 @@ extension ChatViewControllerTests {
fakeDataSource.chatItems = createFakeChatItems(count: 2)
controller.chatDataSource = fakeDataSource
self.fakeDidAppearAndLayout(controller: controller)
XCTAssertEqual(presenterBuilder.presentersCreatedCount, 2)
presenterBuilder.createdPresenters.forEach { ($0 as! FakePresenter)._isItemUpdateSupportedReturnValue = true }
let numberOfPresentersBeforeUpdate = presenterBuilder.createdPresenters.count

fakeDataSource.chatItems = createFakeChatItems(count: 3)
let asyncExpectation = expectation(description: "update")
Expand All @@ -294,27 +295,57 @@ extension ChatViewControllerTests {
}

self.waitForExpectations(timeout: 1) { _ in
XCTAssertEqual(presenterBuilder.presentersCreatedCount, 3)
let numberOfPresenterAfterUpdate = presenterBuilder.createdPresenters.count
XCTAssertEqual(numberOfPresenterAfterUpdate - numberOfPresentersBeforeUpdate, 1)
}
}

func testThat_WhenDataSourceIsUpdatedWithTheSameItems_ThenNoNewItemPresentersAreCreated() {
func testThat_GivenPresenterSupportsUpdates_WhenDataSourceIsUpdatedWithTheSameItem_ThendNewPresenterIsNotCreated_AndPresenterIsUpdatedOnce() {
let presenterBuilder = FakePresenterBuilder()
let controller = TesteableChatViewController(presenterBuilders: ["fake-type": [presenterBuilder]])
let fakeDataSource = FakeDataSource()
fakeDataSource.chatItems = createFakeChatItems(count: 2)
fakeDataSource.chatItems = createFakeChatItems(count: 1)
controller.chatDataSource = fakeDataSource
self.fakeDidAppearAndLayout(controller: controller)
XCTAssertEqual(presenterBuilder.presentersCreatedCount, 2)
let presenter = presenterBuilder.createdPresenters.last! as! FakePresenter
presenter._isItemUpdateSupportedReturnValue = true
XCTAssertEqual(presenter._updateWithChatItemCallsCount, 0)
XCTAssertEqual(presenterBuilder.createdPresenters.count, 1)

fakeDataSource.chatItems = createFakeChatItems(count: 2)
fakeDataSource.chatItems = createFakeChatItems(count: 1)
let asyncExpectation = expectation(description: "update")
controller.enqueueModelUpdate(updateType: .normal) {
asyncExpectation.fulfill()
}

self.waitForExpectations(timeout: 1) { _ in
XCTAssertEqual(presenterBuilder.presentersCreatedCount, 2)
XCTAssertEqual(presenterBuilder.createdPresenters.count, 1)
XCTAssertEqual(presenter._updateWithChatItemCallsCount, 1)
XCTAssert(presenter._updateWithChatItemLastCallParams! === fakeDataSource.chatItems.first!)
}
}

func testThat_GivenPresenterDoesntSupportUpdates_WhenDataSourceIsUpdatedWithTheSameItem_ThenPresenterIsNotUpdated_AndNewPresenterIsCreated() {
let presenterBuilder = FakePresenterBuilder()
let controller = TesteableChatViewController(presenterBuilders: ["fake-type": [presenterBuilder]])
let fakeDataSource = FakeDataSource()
fakeDataSource.chatItems = createFakeChatItems(count: 1)
controller.chatDataSource = fakeDataSource
self.fakeDidAppearAndLayout(controller: controller)
let presenter = presenterBuilder.createdPresenters.last! as! FakePresenter
presenter._isItemUpdateSupportedReturnValue = false
XCTAssertEqual(presenter._updateWithChatItemCallsCount, 0)
XCTAssertEqual(presenterBuilder.createdPresenters.count, 1)

fakeDataSource.chatItems = createFakeChatItems(count: 1)
let asyncExpectation = expectation(description: "update")
controller.enqueueModelUpdate(updateType: .normal) {
asyncExpectation.fulfill()
}

self.waitForExpectations(timeout: 1) { _ in
XCTAssertFalse(presenter._updateWithChatItemIsCalled)
XCTAssertEqual(presenterBuilder.createdPresenters.count, 2)
}
}
}

0 comments on commit cdbc86b

Please sign in to comment.