Diffable DataSource增改删实现疑问及代码逻辑优化咨询
Hey there! Let's walk through your Diffable DataSource questions and code to get things sorted out. You're already off to a great start with the add/update functionality—let's refine this to align better with Diffable's design and tackle those open questions.
1. Ditch manual array manipulation—use snapshots directly
Right now, you're pulling items from the current snapshot into an array, modifying that array, then passing it back to updateData. While this works, it skips a lot of Diffable DataSource's built-in efficiency. Instead, you should work directly with snapshots to modify your data state:
Here's how you can rewrite your save method to use snapshots properly:
func save(_ project: Project, withImage image: UIImage?) { var projectToBeSaved = project projectToBeSaved.lastEdited = Date() // Grab the current snapshot from the data source var currentSnapshot = dataSource.snapshot() if currentSnapshot.contains(projectToBeSaved) { // For existing projects: remove the old entry and add the updated one currentSnapshot.deleteItems([projectToBeSaved]) currentSnapshot.appendItems([projectToBeSaved], toSection: .normal) } else { // For new projects: just append to the section currentSnapshot.appendItems([projectToBeSaved], toSection: .normal) } // Since snapshots don't support sorting directly, we'll create a new sorted snapshot let sortedItems = currentSnapshot.itemIdentifiers.sorted { $0.lastEdited > $1.lastEdited } var sortedSnapshot = NSDiffableDataSourceSnapshot<Section, Project>() sortedSnapshot.appendSections([.normal]) sortedSnapshot.appendItems(sortedItems) // Apply the updated snapshot—no need for reloadData! dataSource.apply(sortedSnapshot, animatingDifferences: true) // TODO: Persist to JSON }
A quick note: If your Project class properly implements Identifiable (with a stable, unique id property), you could also use currentSnapshot.reloadItems([projectToBeSaved]) to update existing entries instead of deleting/adding. But since you need sorting, rebuilding the sorted snapshot is the way to go.
2. Should you move all save logic to updateData?
Not exactly—but you can refactor to separate concerns for cleaner code. Instead of pulling items from the snapshot every time, maintain a single source of truth for your data (a projects array in your view controller). Then:
- Let
savehandle modifying this array, sorting, and persistence - Let
updateDatahandle turning that array into a snapshot and updating the UI
Here's what that refactored setup would look like:
class ProjectsViewController: UIViewController { // This is your single source of truth for project data private var projects: [Project] = [] private var dataSource: UICollectionViewDiffableDataSource<Section, Project>! // Simplified updateData that uses your source-of-truth array func updateData() { var snapshot = NSDiffableDataSourceSnapshot<Section, Project>() snapshot.appendSections([.normal]) snapshot.appendItems(projects) dataSource.apply(snapshot, animatingDifferences: true) } // ... rest of your view controller setup } extension ProjectsViewController: ProjectHandler { func save(_ project: Project, withImage image: UIImage?) { var projectToBeSaved = project projectToBeSaved.lastEdited = Date() // Update the source-of-truth array if let index = projects.firstIndex(of: projectToBeSaved) { projects[index] = projectToBeSaved } else { projects.append(projectToBeSaved) } // Sort the array projects.sort { $0.lastEdited > $1.lastEdited } // Persist to JSON (extract this to a helper method if you want!) saveProjectsToJSON() // Trigger UI update updateData() // REMOVE collectionView.reloadData() — apply() handles UI updates with animation! } private func saveProjectsToJSON() { // TODO: Implement your JSON persistence logic here } }
This separation makes your code easier to debug and maintain—you always know exactly where your data is being modified and where the UI is being updated.
3. How to implement delete functionality
Following the source-of-truth pattern above, deleting is straightforward:
func deleteProject(_ project: Project) { // Remove from source-of-truth array projects.removeAll { $0.id == project.id } // Persist the change saveProjectsToJSON() // Update UI updateData() // Alternatively, if you want to work directly with snapshots (sync the array afterward!): // var snapshot = dataSource.snapshot() // snapshot.deleteItems([project]) // dataSource.apply(snapshot) // projects = snapshot.itemIdentifiers // saveProjectsToJSON() }
4. Quick critical fixes
- Delete
collectionView.reloadData(): Calling this negates all of Diffable DataSource's efficiency and animation benefits. Theapply(snapshot)method already handles updating the UI properly. - Ensure
ProjectisIdentifiable: This is non-negotiable for Diffable to work correctly. Make sure yourProjectclass has a unique, stableidproperty (like a UUID) so Diffable can track changes accurately. - Keep persistence separate: Extract your JSON saving logic to a helper method (like
saveProjectsToJSON()) to keep yoursavemethod focused on business logic.
内容的提问来源于stack exchange,提问作者DeveloperSammy

