@@ -17,15 +17,17 @@ class ConfigureNotificationsViewController: UIViewController {
17
17
private let logger = Logger ( subsystem: " com.beeminder.beeminder " , category: " ConfigureNotificationsViewController " )
18
18
19
19
private var lastFetched : Date ?
20
- fileprivate var goals : [ Goal ] = [ ]
21
- fileprivate var cellReuseIdentifier = " configureNotificationsTableViewCell "
22
20
fileprivate var tableView = UITableView ( )
23
21
fileprivate let settingsButton = BSButton ( )
24
22
private let goalManager : GoalManager
25
23
private let viewContext : NSManagedObjectContext
26
24
private let currentUserManager : CurrentUserManager
27
25
private let requestManager : RequestManager
28
26
27
+ private lazy var dataSource : NotificationsTableViewDiffibleDataSource = {
28
+ NotificationsTableViewDiffibleDataSource ( goals: [ ] , tableView: tableView)
29
+ } ( )
30
+
29
31
init ( goalManager: GoalManager , viewContext: NSManagedObjectContext , currentUserManager: CurrentUserManager , requestManager: RequestManager ) {
30
32
self . goalManager = goalManager
31
33
self . viewContext = viewContext
@@ -65,21 +67,33 @@ class ConfigureNotificationsViewController: UIViewController {
65
67
}
66
68
self . tableView. isHidden = true
67
69
self . tableView. delegate = self
68
- self . tableView. dataSource = self
70
+ self . tableView. dataSource = self . dataSource
69
71
self . tableView. refreshControl = {
70
72
let refresh = UIRefreshControl ( )
71
73
refresh. addTarget ( self , action: #selector( fetchGoals) , for: . valueChanged)
72
74
return refresh
73
75
} ( )
74
76
self . tableView. tableFooterView = UIView ( )
75
- self . tableView. register ( SettingsTableViewCell . self, forCellReuseIdentifier: self . cellReuseIdentifier)
76
77
self . fetchGoals ( )
77
78
self . updateHiddenElements ( )
78
79
NotificationCenter . default. addObserver ( self , selector: #selector( self . foregroundEntered) , name: UIApplication . willEnterForegroundNotification, object: nil )
79
80
}
80
81
81
- override func didReceiveMemoryWarning( ) {
82
- super. didReceiveMemoryWarning ( )
82
+ override func viewWillAppear( _ animated: Bool ) {
83
+ super. viewWillAppear ( animated)
84
+
85
+ self . applySnapshot ( )
86
+ }
87
+
88
+ private func applySnapshot( ) {
89
+ guard lastFetched != nil else {
90
+ let snapshot = NSDiffableDataSourceSnapshot < NotificationsTableViewDiffibleDataSource . Section , String > ( )
91
+ dataSource. apply ( snapshot)
92
+ return
93
+ }
94
+
95
+ let snapshot = dataSource. makeSnapshot ( )
96
+ dataSource. apply ( snapshot, animatingDifferences: true )
83
97
}
84
98
85
99
@objc func settingsButtonTapped( ) {
@@ -112,7 +126,7 @@ class ConfigureNotificationsViewController: UIViewController {
112
126
MBProgressHUD . showAdded ( to: self . view, animated: true )
113
127
do {
114
128
try await self . goalManager. refreshGoals ( )
115
- self . goals = self . goalManager. staleGoals ( context: self . viewContext) ? . sorted ( by : { $0 . slug < $1 . slug } ) ?? [ ]
129
+ self . dataSource . goals = self . goalManager. staleGoals ( context: self . viewContext) ? . sorted ( using : SortDescriptor ( \ . slug) ) ?? [ ]
116
130
self . lastFetched = Date ( )
117
131
MBProgressHUD . hide ( for: self . view, animated: true )
118
132
} catch {
@@ -125,102 +139,128 @@ class ConfigureNotificationsViewController: UIViewController {
125
139
self . present ( alert, animated: true , completion: nil )
126
140
}
127
141
}
128
- self . tableView . reloadData ( )
142
+ self . applySnapshot ( )
129
143
}
130
144
}
131
145
}
132
146
133
- private extension ConfigureNotificationsViewController {
147
+ private class NotificationsTableViewDiffibleDataSource : UITableViewDiffableDataSource < NotificationsTableViewDiffibleDataSource . Section , String > {
148
+ static let cellReuseIdentifier = " configureNotificationsTableViewCell "
149
+
150
+ var goals : [ Goal ]
151
+
152
+ enum Section : Int , CaseIterable {
153
+ case defaultNotificationSettings = 0
154
+ case goalsUsingCustomSettings = 1
155
+ case goalsUsingDefaults = 2
156
+ }
157
+
158
+ override func tableView( _ tableView: UITableView , titleForHeaderInSection section: Int ) -> String ? {
159
+ guard let section = Section ( rawValue: section) else { return nil }
160
+
161
+ return switch section {
162
+ case . defaultNotificationSettings:
163
+ " Defaults "
164
+ case . goalsUsingDefaults where goalsUsingDefaultNotifications. isEmpty:
165
+ nil
166
+ case . goalsUsingCustomSettings where goalsUsingNonDefaultNotifications. isEmpty:
167
+ nil
168
+ case . goalsUsingDefaults:
169
+ " Using Defaults "
170
+ case . goalsUsingCustomSettings:
171
+ " Customized "
172
+ }
173
+ }
174
+
175
+ init ( goals: [ Goal ] , tableView: UITableView ) {
176
+ self . goals = goals
177
+ tableView. register ( SettingsTableViewCell . self, forCellReuseIdentifier: Self . cellReuseIdentifier)
178
+
179
+ super. init ( tableView: tableView) { tableView, indexPath, title in
180
+ guard
181
+ let cell = tableView. dequeueReusableCell ( withIdentifier: Self . cellReuseIdentifier, for: indexPath) as? SettingsTableViewCell
182
+ else { return UITableViewCell ( ) }
183
+
184
+ cell. title = title
185
+ return cell
186
+ }
187
+ }
188
+
134
189
var goalsUsingDefaultNotifications : [ Goal ] {
135
190
self . goals. filter { $0. useDefaults }
136
191
}
137
192
138
193
var goalsUsingNonDefaultNotifications : [ Goal ] {
139
194
self . goals. filter { !$0. useDefaults }
140
195
}
196
+
197
+ func makeSnapshot( ) -> NSDiffableDataSourceSnapshot < Section , String > {
198
+ var snapshot = NSDiffableDataSourceSnapshot < Section , String > ( )
199
+
200
+ snapshot. appendSections ( [ . defaultNotificationSettings] )
201
+ snapshot. appendItems ( [ " Default notification settings " ] ,
202
+ toSection: . defaultNotificationSettings)
141
203
142
- }
204
+ snapshot. appendSections ( [ . goalsUsingCustomSettings] )
205
+ snapshot. appendItems ( goalsUsingNonDefaultNotifications. map { $0. slug } ,
206
+ toSection: . goalsUsingCustomSettings)
207
+
208
+ snapshot. appendSections ( [ . goalsUsingDefaults] )
209
+ snapshot. appendItems ( goalsUsingDefaultNotifications. map { $0. slug } ,
210
+ toSection: . goalsUsingDefaults)
143
211
144
- extension ConfigureNotificationsViewController : UITableViewDataSource , UITableViewDelegate {
145
- func numberOfSections( in tableView: UITableView ) -> Int {
146
- guard lastFetched != nil else { return 0 }
147
- return 3
148
- }
149
-
150
- func tableView( _ tableView: UITableView , numberOfRowsInSection section: Int ) -> Int {
151
- switch section {
152
- case 0 :
153
- return 1
154
- case 1 :
155
- return self . goalsUsingDefaultNotifications. count
156
- default :
157
- return self . goalsUsingNonDefaultNotifications. count
158
- }
159
- }
160
-
161
- func tableView( _ tableView: UITableView , titleForHeaderInSection section: Int ) -> String ? {
162
- switch section {
163
- case 0 :
164
- guard !goals. isEmpty else { return nil }
165
- return " Defaults "
166
- case 1 :
167
- guard !goalsUsingDefaultNotifications. isEmpty else { return nil }
168
- return " Using Defaults "
169
- default :
170
- guard !goalsUsingNonDefaultNotifications. isEmpty else { return nil }
171
- return " Customized "
172
- }
212
+ snapshot. reconfigureItems ( goals. map ( { $0. slug } ) )
213
+
214
+ return snapshot
173
215
}
174
216
175
- func tableView( _ tableView: UITableView , cellForRowAt indexPath: IndexPath ) -> UITableViewCell {
176
- guard let cell = tableView. dequeueReusableCell ( withIdentifier: self . cellReuseIdentifier) as? SettingsTableViewCell else {
177
- return UITableViewCell ( )
217
+ func goalAtIndexPath( _ indexPath: IndexPath ) -> Goal ? {
218
+ guard
219
+ let section = NotificationsTableViewDiffibleDataSource . Section ( rawValue: indexPath. section)
220
+ else {
221
+ return nil
178
222
}
179
223
180
- switch indexPath. section {
181
- case 0 :
182
- cell. title = " Default notification settings "
183
- return cell
184
- case 1 :
185
- let goal = self . goalsUsingDefaultNotifications [ indexPath. row]
186
- cell. title = goal. slug
187
- return cell
188
- default :
189
- let goal = self . goalsUsingNonDefaultNotifications [ indexPath. row]
190
- cell. title = goal. slug
191
- return cell
224
+ return switch section {
225
+ case . defaultNotificationSettings:
226
+ nil
227
+ case . goalsUsingCustomSettings:
228
+ indexPath. row < goalsUsingNonDefaultNotifications. count ? goalsUsingNonDefaultNotifications [ indexPath. row] : nil
229
+ case . goalsUsingDefaults:
230
+ indexPath. row < goalsUsingDefaultNotifications. count ? goalsUsingDefaultNotifications [ indexPath. row] : nil
192
231
}
193
232
}
194
-
233
+ }
234
+
235
+ extension ConfigureNotificationsViewController : UITableViewDelegate {
195
236
func tableView( _ tableView: UITableView , didSelectRowAt indexPath: IndexPath ) {
196
- let editNotificationsVC : UIViewController
237
+ self . tableView . deselectRow ( at : indexPath , animated : true )
197
238
198
- switch indexPath. section {
199
- case 0 :
200
- editNotificationsVC = EditDefaultNotificationsViewController (
201
- currentUserManager: currentUserManager,
202
- requestManager: requestManager,
203
- goalManager: goalManager,
204
- viewContext: viewContext)
205
- case 1 :
206
- let goal = self . goalsUsingDefaultNotifications [ indexPath. row]
207
- editNotificationsVC = EditGoalNotificationsViewController (
208
- goal: goal,
209
- currentUserManager: currentUserManager,
210
- requestManager: requestManager,
211
- goalManager: goalManager,
212
- viewContext: viewContext)
213
- default :
214
- let goal = self . goalsUsingNonDefaultNotifications [ indexPath. row]
215
- editNotificationsVC = EditGoalNotificationsViewController (
216
- goal: goal,
217
- currentUserManager: currentUserManager,
218
- requestManager: requestManager,
219
- goalManager: goalManager,
220
- viewContext: viewContext)
239
+ guard let section = NotificationsTableViewDiffibleDataSource . Section ( rawValue: indexPath. section) else {
240
+ return
221
241
}
222
242
223
- self . navigationController? . pushViewController ( editNotificationsVC, animated: true )
224
- self . tableView. deselectRow ( at: indexPath, animated: true )
243
+ var editNotificationsVC : UIViewController ? {
244
+ switch section {
245
+ case . defaultNotificationSettings:
246
+ return EditDefaultNotificationsViewController (
247
+ currentUserManager: currentUserManager,
248
+ requestManager: requestManager,
249
+ goalManager: goalManager,
250
+ viewContext: viewContext)
251
+ case . goalsUsingDefaults, . goalsUsingCustomSettings:
252
+ guard let goal = self . dataSource. goalAtIndexPath ( indexPath) else { return nil }
253
+ return EditGoalNotificationsViewController (
254
+ goal: goal,
255
+ currentUserManager: currentUserManager,
256
+ requestManager: requestManager,
257
+ goalManager: goalManager,
258
+ viewContext: viewContext)
259
+ }
260
+ }
261
+
262
+ if let editNotificationsVC {
263
+ self . navigationController? . pushViewController ( editNotificationsVC, animated: true )
264
+ }
225
265
}
226
266
}
0 commit comments