From 6ec5b742efba03080a470cfc4ca5e77134cbd83c Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Mon, 18 Jul 2022 13:01:04 +0100
Subject: [PATCH] Add CellFactory

---
 Sources/CollectionView/CellFactory.swift      | 64 ++++++++++++++++
 .../CellFactoryTests.swift                    | 75 +++++++++++++++++++
 2 files changed, 139 insertions(+)
 create mode 100644 Sources/CollectionView/CellFactory.swift
 create mode 100644 Tests/CollectionViewTests/CellFactoryTests.swift

diff --git a/Sources/CollectionView/CellFactory.swift b/Sources/CollectionView/CellFactory.swift
new file mode 100644
index 00000000..00faf2c2
--- /dev/null
+++ b/Sources/CollectionView/CellFactory.swift
@@ -0,0 +1,64 @@
+import UIKit
+
+public struct CellFactory<Model> {
+  public struct Registrar {
+    public var register: (UICollectionView) -> Void
+
+    public func callAsFunction(in view: UICollectionView) {
+      register(view)
+    }
+  }
+
+  public struct Builder {
+    public var buildCell: (Model, UICollectionView, IndexPath) -> UICollectionViewCell?
+
+    public func callAsFunction(
+      for model: Model,
+      in view: UICollectionView,
+      at indexPath: IndexPath
+    ) -> UICollectionViewCell? {
+      buildCell(model, view, indexPath)
+    }
+  }
+
+  public var register: Registrar
+  public var build: Builder
+
+  public init(
+    register: Registrar,
+    build: Builder
+  ) {
+    self.register = register
+    self.build = build
+  }
+}
+
+extension CellFactory {
+  public static func combined(_ factories: CellFactory...) -> CellFactory {
+    combined(factories)
+  }
+
+  public static func combined(_ factories: [CellFactory]) -> CellFactory {
+    CellFactory(
+      register: .init { collectionView in
+        factories.forEach { $0.register(in: collectionView) }
+      },
+      build: .init { model, collectionView, indexPath in
+        factories.lazy
+          .compactMap { $0.build(for: model, in: collectionView, at: indexPath) }
+          .first
+      }
+    )
+  }
+}
+
+#if DEBUG
+extension CellFactory {
+  public static func failing() -> CellFactory {
+    CellFactory(
+      register: .init { _ in fatalError("Not implemented") },
+      build: .init { _, _, _ in fatalError("Not implemented") }
+    )
+  }
+}
+#endif
diff --git a/Tests/CollectionViewTests/CellFactoryTests.swift b/Tests/CollectionViewTests/CellFactoryTests.swift
new file mode 100644
index 00000000..c9b052dd
--- /dev/null
+++ b/Tests/CollectionViewTests/CellFactoryTests.swift
@@ -0,0 +1,75 @@
+import XCTest
+@testable import CollectionView
+
+final class CellFactoryTests: XCTestCase {
+  func testCombined() {
+    var didRegisterFirst = [UICollectionView]()
+    var didRegisterSecond = [UICollectionView]()
+    var didRegisterThird = [UICollectionView]()
+
+    class Cell: UICollectionViewCell {
+      var collectionView: UICollectionView?
+      var indexPath: IndexPath?
+    }
+
+    let factory = CellFactory<Int>.combined(
+      .init(
+        register: .init { didRegisterFirst.append($0) },
+        build: .init { model, collectionView, indexPath in
+          guard model == 1 else { return nil }
+          let cell = Cell()
+          cell.collectionView = collectionView
+          cell.indexPath = indexPath
+          return cell
+        }
+      ),
+      .init(
+        register: .init { didRegisterSecond.append($0) },
+        build: .init { model, collectionView, indexPath in
+          guard model == 2 else { return nil }
+          let cell = Cell()
+          cell.collectionView = collectionView
+          cell.indexPath = indexPath
+          return cell
+        }
+      ),
+      .init(
+        register: .init { didRegisterThird.append($0) },
+        build: .init { model, collectionView, indexPath in
+          guard model == 3 else { return nil }
+          let cell = Cell()
+          cell.collectionView = collectionView
+          cell.indexPath = indexPath
+          return cell
+        }
+      )
+    )
+
+    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init())
+
+    factory.register(in: collectionView)
+
+    XCTAssertEqual(didRegisterFirst, [collectionView])
+    XCTAssertEqual(didRegisterSecond, [collectionView])
+    XCTAssertEqual(didRegisterThird, [collectionView])
+
+    let firstCell = factory.build(for: 1, in: collectionView, at: IndexPath(item: 0, section: 1)) as? Cell
+
+    XCTAssertEqual(firstCell?.collectionView, collectionView)
+    XCTAssertEqual(firstCell?.indexPath, IndexPath(item: 0, section: 1))
+
+    let secondCell = factory.build(for: 2, in: collectionView, at: IndexPath(item: 2, section: 3)) as? Cell
+
+    XCTAssertEqual(secondCell?.collectionView, collectionView)
+    XCTAssertEqual(secondCell?.indexPath, IndexPath(item: 2, section: 3))
+
+    let thirdCell = factory.build(for: 3, in: collectionView, at: IndexPath(item: 4, section: 5)) as? Cell
+
+    XCTAssertEqual(thirdCell?.collectionView, collectionView)
+    XCTAssertEqual(thirdCell?.indexPath, IndexPath(item: 4, section: 5))
+
+    let otherCell = factory.build(for: 4, in: collectionView, at: IndexPath(item: 0, section: 0))
+
+    XCTAssertNil(otherCell)
+  }
+}
-- 
GitLab