-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathCellViewModel.swift
136 lines (106 loc) · 4.17 KB
/
CellViewModel.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//
// Created by Jesse Squires
// https://www.jessesquires.com
//
// Documentation
// https://jessesquires.github.io/ReactiveCollectionsKit
//
// GitHub
// https://github.com/jessesquires/ReactiveCollectionsKit
//
// Copyright © 2019-present Jesse Squires
//
import Foundation
import UIKit
/// Defines a view model that describes and configures a cell in the collection view.
@MainActor
public protocol CellViewModel: DiffableViewModel, ViewRegistrationProvider {
/// The type of cell that this view model represents and configures.
associatedtype CellType: UICollectionViewCell
var shouldHighlight: Bool { get }
var contextMenuConfiguration: UIContextMenuConfiguration? { get }
func configure(cell: CellType)
func didSelect(with coordinator: CellEventCoordinator?)
}
extension CellViewModel {
public var shouldHighlight: Bool { true }
public var contextMenuConfiguration: UIContextMenuConfiguration? { nil }
public func didSelect(with coordinator: CellEventCoordinator?) {
coordinator?.didSelectCell(viewModel: self)
}
}
extension CellViewModel {
/// The cell class for this view model.
public var cellClass: AnyClass { CellType.self }
/// A default reuse identifier for cell registration.
/// Value defaults to the name of the class implementing the `CellViewModel` protocol.
public var reuseIdentifier: String { "\(Self.self)" }
/// A default registration for this view model for class-based cells.
/// - Warning: Does not work for nib-based cells.
public var registration: ViewRegistration {
ViewRegistration(
reuseIdentifier: self.reuseIdentifier,
cellClass: self.cellClass
)
}
/// Returns a type-erased version of this view model.
public var anyViewModel: AnyCellViewModel {
AnyCellViewModel(self)
}
public func dequeueAndConfigureCellFor(collectionView: UICollectionView, at indexPath: IndexPath) -> CellType {
let cell = self.registration.dequeueViewFor(collectionView: collectionView, at: indexPath) as! CellType
self.configure(cell: cell)
return cell
}
}
/// A type-erased cell view model.
@MainActor
public struct AnyCellViewModel: CellViewModel {
// MARK: DiffableViewModel
public var id: UniqueIdentifier { self._id }
// MARK: ViewRegistrationProvider
public var registration: ViewRegistration { self._registration }
// MARK: CellViewModel
public typealias CellType = UICollectionViewCell
public var shouldHighlight: Bool { self._shouldHighlight }
public var contextMenuConfiguration: UIContextMenuConfiguration? { self._contextMenuConfiguration }
public func configure(cell: UICollectionViewCell) {
self._configure(cell)
}
public func didSelect(with coordinator: CellEventCoordinator?) {
self._didSelect(coordinator)
}
// MARK: Private
private let _viewModel: AnyHashable
private let _id: UniqueIdentifier
private let _registration: ViewRegistration
private let _shouldHighlight: Bool
private let _contextMenuConfiguration: UIContextMenuConfiguration?
private let _configure: (CellType) -> Void
private let _didSelect: (CellEventCoordinator?) -> Void
// MARK: Init
public init<T: CellViewModel>(_ viewModel: T) {
self._viewModel = viewModel
self._id = viewModel.id
self._registration = viewModel.registration
self._shouldHighlight = viewModel.shouldHighlight
self._contextMenuConfiguration = viewModel.contextMenuConfiguration
self._configure = { cell in
precondition(cell is T.CellType, "Cell must be of type \(T.CellType.self). Found \(cell.self)")
viewModel.configure(cell: cell as! T.CellType)
}
self._didSelect = { controller in
viewModel.didSelect(with: controller)
}
}
}
extension AnyCellViewModel: Equatable {
nonisolated public static func == (left: AnyCellViewModel, right: AnyCellViewModel) -> Bool {
left._viewModel == right._viewModel
}
}
extension AnyCellViewModel: Hashable {
nonisolated public func hash(into hasher: inout Hasher) {
self._viewModel.hash(into: &hasher)
}
}