Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-20 - Batching @Published Array Mutations
**Learning:** In SwiftUI ObservableObject view models, mutating individual elements of a @Published array property inside a loop triggers a UI update notification for every change.
**Action:** Use functional methods like `map` to batch updates into a single property assignment, significantly reducing unnecessary UI recalculations. Always add comments explaining this optimization.
39 changes: 31 additions & 8 deletions Sources/Cacheout/ViewModels/CacheoutViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,22 @@ class CacheoutViewModel: ObservableObject {
}

func selectAllSafe() {
for i in scanResults.indices where scanResults[i].category.riskLevel == .safe && !scanResults[i].isEmpty {
scanResults[i].isSelected = true
// Optimization: Batch @Published array update to prevent O(N) UI re-renders
scanResults = scanResults.map { result in
var copy = result
if copy.category.riskLevel == .safe && !copy.isEmpty {
copy.isSelected = true
}
return copy
}
}

func deselectAll() {
for i in scanResults.indices {
scanResults[i].isSelected = false
// Optimization: Batch @Published array update to prevent O(N) UI re-renders
scanResults = scanResults.map { result in
var copy = result
copy.isSelected = false
return copy
}
deselectAllNodeModules()
}
Expand All @@ -193,17 +201,32 @@ class CacheoutViewModel: ObservableObject {
}

func selectStaleNodeModules() {
for i in nodeModulesItems.indices where nodeModulesItems[i].isStale {
nodeModulesItems[i].isSelected = true
// Optimization: Batch @Published array update to prevent O(N) UI re-renders
nodeModulesItems = nodeModulesItems.map { item in
var copy = item
if copy.isStale {
copy.isSelected = true
}
return copy
}
}

func selectAllNodeModules() {
for i in nodeModulesItems.indices { nodeModulesItems[i].isSelected = true }
// Optimization: Batch @Published array update to prevent O(N) UI re-renders
nodeModulesItems = nodeModulesItems.map { item in
var copy = item
copy.isSelected = true
return copy
}
}

func deselectAllNodeModules() {
for i in nodeModulesItems.indices { nodeModulesItems[i].isSelected = false }
// Optimization: Batch @Published array update to prevent O(N) UI re-renders
nodeModulesItems = nodeModulesItems.map { item in
var copy = item
copy.isSelected = false
return copy
}
}

/// Menu bar label: show free GB in the tray
Expand Down