Skip to content

Commit 3c3485d

Browse files
committed
docs + API refinements
1 parent 4cceadc commit 3c3485d

6 files changed

+141
-82
lines changed

Sources/CollectionViewDriver.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public final class CollectionViewDriver: NSObject {
135135
self._dataSource.reload(viewModel, completion: nil)
136136
}
137137

138-
// MARK: State information
138+
// MARK: State Information
139139

140140
/// The number of sections displayed by the collection view.
141141
var numberOfSections: Int {

Sources/CollectionViewModel.swift

+47-29
Original file line numberDiff line numberDiff line change
@@ -39,56 +39,45 @@ public struct CollectionViewModel: Hashable, DiffableViewModel {
3939
self.sections = sections.filter { $0.isNotEmpty }
4040
}
4141

42-
// MARK: Accessing View Registration Info
43-
44-
/// Returns all the `ViewRegistration` objects for the collection.
45-
public func allRegistrations() -> Set<ViewRegistration> {
46-
var all = Set<ViewRegistration>()
47-
self.sections.forEach {
48-
all.formUnion($0.allRegistrations)
49-
}
50-
return all
51-
}
52-
5342
// MARK: Accessing Sections
5443

55-
/// Returns a dictionary of all sections keyed by their `id`.
56-
public func allSectionsByIdentifier() -> [UniqueIdentifier: SectionViewModel] {
57-
let tuples = self.sections.map { ($0.id, $0) }
58-
return Dictionary(uniqueKeysWithValues: tuples)
59-
}
60-
6144
/// Returns the section for the specified `id`.
6245
/// - Parameter id: The identifier for the section.
6346
/// - Returns: The section, if it exists.
6447
public func sectionViewModel(for id: UniqueIdentifier) -> SectionViewModel? {
65-
self.allSectionsByIdentifier()[id]
48+
self.sections.first { $0.id == id }
6649
}
6750

68-
// MARK: Accessing Cells
69-
70-
/// Returns a dictionary of all cells keyed by their `id`.
71-
public func allCellsByIdentifier() -> [UniqueIdentifier: AnyCellViewModel] {
72-
let allCells = self.flatMap { $0.cells }
73-
let tuples = allCells.map { ($0.id, $0) }
74-
return Dictionary(uniqueKeysWithValues: tuples)
51+
/// Returns the section at the specified index.
52+
///
53+
/// - Parameter index: The index of the section.
54+
/// - Returns: The section at `index`.
55+
///
56+
/// - Precondition: The specified `index` must be valid.
57+
public func sectionViewModel(at index: Int) -> SectionViewModel {
58+
precondition(index < self.count)
59+
return self.sections[index]
7560
}
7661

62+
// MARK: Accessing Cells
63+
7764
/// Returns the cell for the specified `id`.
65+
///
7866
/// - Parameter id: The identifier for the cell.
7967
/// - Returns: The cell, if it exists.
8068
public func cellViewModel(for id: UniqueIdentifier) -> AnyCellViewModel? {
81-
self.allCellsByIdentifier()[id]
69+
self.flatMap { $0.cells }.first { $0.id == id }
8270
}
8371

8472
/// Returns the cell at the specified index path.
85-
/// - Parameter indexPath: The index path for the cell.
73+
///
74+
/// - Parameter indexPath: The index path of the cell.
8675
/// - Returns: The cell at `indexPath`.
8776
///
8877
/// - Precondition: The specified `indexPath` must be valid.
8978
public func cellViewModel(at indexPath: IndexPath) -> AnyCellViewModel {
9079
precondition(indexPath.section < self.count)
91-
let section = self[indexPath.section]
80+
let section = self.sectionViewModel(at: indexPath.section)
9281

9382
let cells = section.cells
9483
precondition(indexPath.item < cells.count)
@@ -98,6 +87,14 @@ public struct CollectionViewModel: Hashable, DiffableViewModel {
9887

9988
// MARK: Accessing Supplementary Views
10089

90+
/// Returns the supplementary view for the specified `id`.
91+
///
92+
/// - Parameter id: The identifier for the supplementary view.
93+
/// - Returns: The supplementary view, if it exists.
94+
public func supplementaryViewModel(for id: UniqueIdentifier) -> AnySupplementaryViewModel? {
95+
self.flatMap { $0.supplementaryViews }.first { $0.id == id }
96+
}
97+
10198
/// Returns the supplementary view for the specified kind and index path.
10299
/// - Parameters:
103100
/// - kind: The kind of the supplementary view.
@@ -107,7 +104,7 @@ public struct CollectionViewModel: Hashable, DiffableViewModel {
107104
/// - Precondition: The specified `indexPath` must be valid.
108105
public func supplementaryViewModel(ofKind kind: String, at indexPath: IndexPath) -> AnySupplementaryViewModel? {
109106
precondition(indexPath.section < self.count)
110-
let section = self[indexPath.section]
107+
let section = self.sectionViewModel(at: indexPath.section)
111108

112109
if kind == section.header?._kind {
113110
return section.header
@@ -124,6 +121,27 @@ public struct CollectionViewModel: Hashable, DiffableViewModel {
124121

125122
return nil
126123
}
124+
125+
// MARK: Internal
126+
127+
func allRegistrations() -> Set<ViewRegistration> {
128+
var all = Set<ViewRegistration>()
129+
self.sections.forEach {
130+
all.formUnion($0.allRegistrations())
131+
}
132+
return all
133+
}
134+
135+
func allSectionsByIdentifier() -> [UniqueIdentifier: SectionViewModel] {
136+
let tuples = self.sections.map { ($0.id, $0) }
137+
return Dictionary(uniqueKeysWithValues: tuples)
138+
}
139+
140+
func allCellsByIdentifier() -> [UniqueIdentifier: AnyCellViewModel] {
141+
let allCells = self.flatMap { $0.cells }
142+
let tuples = allCells.map { ($0.id, $0) }
143+
return Dictionary(uniqueKeysWithValues: tuples)
144+
}
127145
}
128146

129147
// MARK: Collection, RandomAccessCollection

Sources/DiffableDataSource.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ final class DiffableDataSource: UICollectionViewDiffableDataSource<AnyHashable,
209209
}
210210

211211
// Third, check all supplementary views.
212-
let allSourceSectionSupplementaryViews = sourceSection.allSupplementaryViewsByIdentifier
212+
let allSourceSectionSupplementaryViews = sourceSection.allSupplementaryViewsByIdentifier()
213213

214214
for viewIndex in 0..<destinationSection.supplementaryViews.count {
215215
let destinationView = destinationSection.supplementaryViews[viewIndex]

Sources/DiffableViewModel.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ import Foundation
1616
/// The unique identifier type for a `DiffableViewModel`.
1717
public typealias UniqueIdentifier = AnyHashable
1818

19-
/// Describes a view model that is diffable.
19+
/// Describes a view model that is uniquely identifiable and diffable.
2020
@MainActor
2121
public protocol DiffableViewModel: Identifiable, Hashable {
22-
2322
/// An identifier that uniquely identifies this instance.
2423
var id: UniqueIdentifier { get }
2524
}

Sources/EmptyViewProvider.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414
import Foundation
1515
import UIKit
1616

17-
/// Provides an "empty state" or "no content" view for a collection view.
17+
/// Provides an "empty state" or "no content" view for the collection view.
1818
@MainActor
1919
public struct EmptyViewProvider {
20-
2120
/// A closure that returns the view.
2221
public let viewBuilder: () -> UIView
2322

Sources/SectionViewModel.swift

+90-47
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,42 @@
1313

1414
import Foundation
1515

16-
/// Represents a section of items in a collection or list.
16+
/// Represents a section of items in a collection.
1717
@MainActor
1818
public struct SectionViewModel: DiffableViewModel {
19+
// MARK: DiffableViewModel
1920

21+
/// A unique id for this model.
2022
public let id: UniqueIdentifier
2123

24+
// MARK: Properties
25+
26+
/// The cells in the section.
2227
public let cells: [AnyCellViewModel]
2328

29+
/// The header for the section.
2430
public let header: AnySupplementaryViewModel?
2531

32+
/// The footer for the section.
2633
public let footer: AnySupplementaryViewModel?
2734

35+
/// The supplementary views in the section.
2836
public let supplementaryViews: [AnySupplementaryViewModel]
2937

30-
public var allSupplementaryViewsByIdentifier: [UniqueIdentifier: AnySupplementaryViewModel] {
31-
let tuples = self.supplementaryViews.map { ($0.id, $0) }
32-
return Dictionary(uniqueKeysWithValues: tuples)
33-
}
34-
38+
/// Returns `true` if the section has supplementary views, `false` otherwise.
3539
public var hasSupplementaryViews: Bool {
3640
self.header != nil
3741
|| self.footer != nil
3842
|| self.supplementaryViews.isNotEmpty
3943
}
4044

41-
public var cellRegistrations: Set<ViewRegistration> {
42-
Set(self.cells.map { $0.registration })
43-
}
44-
45-
public var headerFooterRegistrations: Set<ViewRegistration> {
46-
Set([self.header, self.footer].compactMap { $0?.registration })
47-
}
48-
49-
public var supplementaryViewRegistrations: Set<ViewRegistration> {
50-
Set(self.supplementaryViews.map { $0.registration })
51-
}
52-
53-
public var allRegistrations: Set<ViewRegistration> {
54-
let cells = self.cellRegistrations
55-
let headerFooter = self.headerFooterRegistrations
56-
let views = self.supplementaryViewRegistrations
57-
return cells.union(views).union(headerFooter)
58-
}
45+
// MARK: Init
5946

47+
/// Initializes a section.
48+
///
49+
/// - Parameters:
50+
/// - id: A unique identifier for the section.
51+
/// - cells: The cells in the section.
6052
public init(id: UniqueIdentifier, cells: [AnyCellViewModel] = []) {
6153
self.init(
6254
id: id,
@@ -67,6 +59,11 @@ public struct SectionViewModel: DiffableViewModel {
6759
)
6860
}
6961

62+
/// Initializes a section.
63+
///
64+
/// - Parameters:
65+
/// - id: A unique identifier for the section.
66+
/// - cells: The cells in the section.
7067
public init<Cell: CellViewModel>(
7168
id: UniqueIdentifier,
7269
cells: [Cell]
@@ -80,6 +77,14 @@ public struct SectionViewModel: DiffableViewModel {
8077
)
8178
}
8279

80+
/// Initializes a section.
81+
///
82+
/// - Parameters:
83+
/// - id: A unique identifier for the section.
84+
/// - cells: The cells in the section.
85+
/// - header: The header for the section.
86+
/// - footer: The footer for the section.
87+
/// - supplementaryViews: The supplementary views in the section.
8388
public init<Header: SupplementaryHeaderViewModel, Footer: SupplementaryFooterViewModel>(
8489
id: UniqueIdentifier,
8590
cells: [AnyCellViewModel] = [],
@@ -96,6 +101,14 @@ public struct SectionViewModel: DiffableViewModel {
96101
)
97102
}
98103

104+
/// Initializes a section.
105+
///
106+
/// - Parameters:
107+
/// - id: A unique identifier for the section.
108+
/// - cells: The cells in the section.
109+
/// - header: The header for the section.
110+
/// - footer: The footer for the section.
111+
/// - supplementaryViews: The supplementary views in the section.
99112
public init<Cell: CellViewModel, Header: SupplementaryHeaderViewModel, Footer: SupplementaryFooterViewModel>(
100113
id: UniqueIdentifier,
101114
cells: [Cell],
@@ -112,24 +125,34 @@ public struct SectionViewModel: DiffableViewModel {
112125
)
113126
}
114127

115-
public init<Cell: CellViewModel,
116-
Header: SupplementaryHeaderViewModel,
117-
Footer: SupplementaryFooterViewModel,
118-
View: SupplementaryViewModel>(
119-
id: UniqueIdentifier,
120-
cells: [Cell],
121-
header: Header?,
122-
footer: Footer?,
123-
supplementaryViews: [View]
124-
) {
125-
self.init(
126-
id: id,
127-
anyCells: cells.map { $0.eraseToAnyViewModel() },
128-
anyHeader: header?.eraseToAnyViewModel(),
129-
anyFooter: footer?.eraseToAnyViewModel(),
130-
anySupplementaryViews: supplementaryViews.map { $0.eraseToAnyViewModel() }
131-
)
132-
}
128+
/// Initializes a section.
129+
///
130+
/// - Parameters:
131+
/// - id: A unique identifier for the section.
132+
/// - cells: The cells in the section.
133+
/// - header: The header for the section.
134+
/// - footer: The footer for the section.
135+
/// - supplementaryViews: The supplementary views in the section.
136+
public init<
137+
Cell: CellViewModel,
138+
Header: SupplementaryHeaderViewModel,
139+
Footer: SupplementaryFooterViewModel,
140+
View: SupplementaryViewModel>
141+
(
142+
id: UniqueIdentifier,
143+
cells: [Cell],
144+
header: Header?,
145+
footer: Footer?,
146+
supplementaryViews: [View]
147+
) {
148+
self.init(
149+
id: id,
150+
anyCells: cells.map { $0.eraseToAnyViewModel() },
151+
anyHeader: header?.eraseToAnyViewModel(),
152+
anyFooter: footer?.eraseToAnyViewModel(),
153+
anySupplementaryViews: supplementaryViews.map { $0.eraseToAnyViewModel() }
154+
)
155+
}
133156

134157
private init(
135158
id: UniqueIdentifier,
@@ -148,10 +171,30 @@ public struct SectionViewModel: DiffableViewModel {
148171
self.supplementaryViews = anySupplementaryViews
149172
}
150173

151-
func supplementaryViewsEqualTo(_ otherSection: Self) -> Bool {
152-
self.header == otherSection.header
153-
&& self.footer == otherSection.footer
154-
&& self.supplementaryViews == otherSection.supplementaryViews
174+
// MARK: Internal
175+
176+
func cellRegistrations() -> Set<ViewRegistration> {
177+
Set(self.cells.map { $0.registration })
178+
}
179+
180+
func headerFooterRegistrations() -> Set<ViewRegistration> {
181+
Set([self.header, self.footer].compactMap { $0?.registration })
182+
}
183+
184+
func supplementaryViewRegistrations() -> Set<ViewRegistration> {
185+
Set(self.supplementaryViews.map { $0.registration })
186+
}
187+
188+
func allRegistrations() -> Set<ViewRegistration> {
189+
let cells = self.cellRegistrations()
190+
let headerFooter = self.headerFooterRegistrations()
191+
let views = self.supplementaryViewRegistrations()
192+
return cells.union(views).union(headerFooter)
193+
}
194+
195+
func allSupplementaryViewsByIdentifier() -> [UniqueIdentifier: AnySupplementaryViewModel] {
196+
let tuples = self.supplementaryViews.map { ($0.id, $0) }
197+
return Dictionary(uniqueKeysWithValues: tuples)
155198
}
156199
}
157200

@@ -241,7 +284,7 @@ extension SectionViewModel: CustomDebugStringConvertible {
241284
}
242285

243286
text.append(" registrations: \n")
244-
self.allRegistrations.forEach {
287+
self.allRegistrations().forEach {
245288
text.append("\t- \($0.reuseIdentifier) (\($0.viewType.kind))\n")
246289
}
247290
text.append(" isEmpty: \(self.isEmpty)\n")

0 commit comments

Comments
 (0)