@@ -40,113 +40,114 @@ extension DiffableDataSource {
40
40
from source: CollectionViewModel ,
41
41
to destination: CollectionViewModel ,
42
42
animated: Bool ,
43
- completion: SnapshotCompletion ? ) {
44
- // Build initial destination snapshot, then make adjustments below.
45
- var destinationSnapshot = DiffableSnapshot ( viewModel : destination )
46
-
47
- // Apply item reloads, then section reloads.
48
- // Reloading a section is required to properly reload headers, footers, and other supplementary views .
49
- // This 2-step process is necessary to preserve collection view animations
50
- // and prevent UIKit data source internal inconsistency exceptions.
51
- //
52
- // If we only reloaded a section (instead of reloading items first),
53
- // there are 2 problems:
54
- // 1. item updates would not animate correctly, because the whole section is reloaded.
55
- // 2 . item inserts/deletes could trigger an internal inconsistency exception .
56
-
57
- // Find and perform item (cell) updates first.
58
- let allSourceCells = source . allCellsByIdentifier
59
- let allDestinationCells = destination . allCellsByIdentifier
60
- var itemsToReload = [ UniqueIdentifier ] ( )
61
-
62
- for (cellId , destinationCell ) in allDestinationCells {
63
- let sourceCell = allSourceCells [ cellId ]
64
-
65
- // If this cell exist in the source, and it has changed, then reload it.
66
- if destinationCell != sourceCell {
67
- itemsToReload . append ( cellId )
68
- }
43
+ completion: SnapshotCompletion ?
44
+ ) {
45
+ // Build initial destination snapshot, then make adjustments below.
46
+ var destinationSnapshot = DiffableSnapshot ( viewModel : destination )
47
+
48
+ // Apply item reloads, then section reloads .
49
+ // Reloading a section is required to properly reload headers, footers, and other supplementary views.
50
+ // This 2-step process is necessary to preserve collection view animations
51
+ // and prevent UIKit data source internal inconsistency exceptions.
52
+ //
53
+ // If we only reloaded a section (instead of reloading items first),
54
+ // there are 2 problems:
55
+ // 1 . item updates would not animate correctly, because the whole section is reloaded .
56
+ // 2. item inserts/deletes could trigger an internal inconsistency exception.
57
+
58
+ // Find and perform item (cell) updates first.
59
+ let allSourceCells = source . allCellsByIdentifier
60
+ let allDestinationCells = destination . allCellsByIdentifier
61
+ var itemsToReload = [ UniqueIdentifier ] ( )
62
+
63
+ for (cellId , destinationCell ) in allDestinationCells {
64
+ let sourceCell = allSourceCells [ cellId ]
65
+
66
+ // If this cell exist in the source, and it has changed, then reload it.
67
+ if destinationCell != sourceCell {
68
+ itemsToReload . append ( cellId )
69
69
}
70
+ }
70
71
71
- destinationSnapshot. reconfigureItems ( itemsToReload)
72
+ destinationSnapshot. reconfigureItems ( itemsToReload)
72
73
73
- // Apply snapshot with item reload updates.
74
- self . applySnapshot ( destinationSnapshot, animated: animated) {
75
- // Once item reloads are complete, find and apply section reloads, if needed.
76
- // This is necessary to update SUPPLEMENTARY VIEWS ONLY.
77
- // Supplementary views do not get reloaded / reconfigured automatically when they change.
78
- // To trigger updates on supplementary views, the section must be reloaded.
79
- // Yes, this kinda sucks.
74
+ // Apply snapshot with item reload updates.
75
+ self . applySnapshot ( destinationSnapshot, animated: animated) {
76
+ // Once item reloads are complete, find and apply section reloads, if needed.
77
+ // This is necessary to update SUPPLEMENTARY VIEWS ONLY.
78
+ // Supplementary views do not get reloaded / reconfigured automatically when they change.
79
+ // To trigger updates on supplementary views, the section must be reloaded.
80
+ // Yes, this kinda sucks.
80
81
81
- let allSourceSections = source. allSectionsByIdentifier
82
+ let allSourceSections = source. allSectionsByIdentifier
82
83
83
- // Only get sections that have supplementary views.
84
- let allDestinationSections = destination. allSectionsByIdentifier. filter { _, value in
85
- value. hasSupplementaryViews
84
+ // Only get sections that have supplementary views.
85
+ let allDestinationSections = destination. allSectionsByIdentifier. filter { _, value in
86
+ value. hasSupplementaryViews
87
+ }
88
+
89
+ // If no sections have supplementary views, there's nothing to do.
90
+ guard allDestinationSections. isNotEmpty else {
91
+ completion ? ( )
92
+ return
93
+ }
94
+
95
+ var sectionsToReload = Set < UniqueIdentifier > ( )
96
+
97
+ // As soon as we find 1 supplementary view in a section that needs reloading,
98
+ // we can mark the whole section for reload and exit early.
99
+ for (sectionId, destinationSection) in allDestinationSections {
100
+ // If this section does not exist in the source, then it is newly inserted.
101
+ // Thus, nothing to do.
102
+ guard let sourceSection = allSourceSections [ sectionId] else {
103
+ continue
86
104
}
87
105
88
- // If no sections have supplementary views, there's nothing to do.
89
- guard allDestinationSections. isNotEmpty else {
90
- completion ? ( )
91
- return
106
+ // If this section exist in the source,
107
+ // and it has changed its SUPPLEMENTARY VIEWS ONLY,
108
+ // then only reload the section.
109
+ // First, check headers and footers.
110
+ if destinationSection. header != sourceSection. header {
111
+ sectionsToReload. insert ( sectionId)
112
+ continue
92
113
}
93
114
94
- var sectionsToReload = Set < UniqueIdentifier > ( )
115
+ if destinationSection. footer != sourceSection. footer {
116
+ sectionsToReload. insert ( sectionId)
117
+ continue
118
+ }
95
119
96
- // As soon as we find 1 supplementary view in a section that needs reloading,
97
- // we can mark the whole section for reload and exit early.
98
- for (sectionId, destinationSection) in allDestinationSections {
99
- // If this section does not exist in the source, then it is newly inserted.
100
- // Thus, nothing to do.
101
- guard let sourceSection = allSourceSections [ sectionId] else {
102
- continue
103
- }
120
+ // Next, check all supplementary views.
121
+ let allSourceSectionSupplementaryViews = sourceSection. allSupplementaryViewsByIdentifier
104
122
105
- // If this section exist in the source,
106
- // and it has changed its SUPPLEMENTARY VIEWS ONLY,
107
- // then only reload the section.
108
- // First, check headers and footers.
109
- if destinationSection. header != sourceSection. header {
110
- sectionsToReload. insert ( sectionId)
123
+ for destinationView in destinationSection. supplementaryViews {
124
+ // If this view does not exist in the source, then it is newly added.
125
+ // Thus, nothing to do.
126
+ guard let sourceView = allSourceSectionSupplementaryViews [ destinationView. id] else {
111
127
continue
112
128
}
113
129
114
- if destinationSection. footer != sourceSection. footer {
130
+ // After finding one view that needs reloading, we can stop,
131
+ // because we have to reload the whole section anyway.
132
+ if destinationView != sourceView {
115
133
sectionsToReload. insert ( sectionId)
116
- continue
117
- }
118
-
119
- // Next, check all supplementary views.
120
- let allSourceSectionSupplementaryViews = sourceSection. allSupplementaryViewsByIdentifier
121
-
122
- for destinationView in destinationSection. supplementaryViews {
123
- // If this view does not exist in the source, then it is newly added.
124
- // Thus, nothing to do.
125
- guard let sourceView = allSourceSectionSupplementaryViews [ destinationView. id] else {
126
- continue
127
- }
128
-
129
- // After finding one view that needs reloading, we can stop,
130
- // because we have to reload the whole section anyway.
131
- if destinationView != sourceView {
132
- sectionsToReload. insert ( sectionId)
133
- break
134
- }
134
+ break
135
135
}
136
136
}
137
+ }
137
138
138
- // If no section changes, ignore and call completion
139
- guard sectionsToReload. isNotEmpty else {
140
- completion ? ( )
141
- return
142
- }
139
+ // If no section changes, ignore and call completion
140
+ guard sectionsToReload. isNotEmpty else {
141
+ completion ? ( )
142
+ return
143
+ }
143
144
144
- destinationSnapshot. reloadSections ( sectionsToReload. toArray)
145
+ destinationSnapshot. reloadSections ( sectionsToReload. toArray)
145
146
146
- // Apply final section updates
147
- self . applySnapshot ( destinationSnapshot, animated: animated, completion: completion)
148
- }
147
+ // Apply final section updates
148
+ self . applySnapshot ( destinationSnapshot, animated: animated, completion: completion)
149
149
}
150
+ }
150
151
151
152
func applySnapshot( _ snapshot: Snapshot , animated: Bool , completion: SnapshotCompletion ? = nil ) {
152
153
self . apply ( snapshot, animatingDifferences: animated, completion: completion)
0 commit comments