I’m building a SwiftUI app where the struct AppGroup is identified by a UUID
and stored in a dictionary. My Task model has appGroupId: UUID?
. In TaskDetailView, I create a custom Binding<AppGroup>
from the store, then navigate to AppGroupDetailView. However, when I tap the NavigationLink, the console spams logs, CPU hits 100%, and it never stabilizes.
Relevant Code
AppGroupStore (simplified)
class AppGroupStore: ObservableObject {
@Published var appGroupsDict: [UUID: AppGroup] = [:]
func updateAppGroup(_ id: UUID, appGroup: AppGroup) {
appGroupsDict[id] = appGroup
}
// Returns a binding so views can directly read/write the AppGroup by id
func getBinding(withId id: UUID?) -> Binding<AppGroup> {
Binding(
get: {
if let id = id {
return self.appGroupsDict[id] ?? .empty
}
return .empty
},
set: { newValue in
print("New value set for \(newValue.name)")
self.updateAppGroup(newValue.id, appGroup: newValue)
}
)
}
// ...
}
AppGroup
is a simple struct:
struct AppGroup: Identifiable, Codable {
let id: UUID
var name: String
var apps: [String]
static let empty = AppGroup(id: UUID(), name: "Empty", apps: [])
}
TaskDetailView (main part)
struct TaskDetailView: View {
@Binding var task: ToDoTask // has task.appGroupId: UUID?
@EnvironmentObject var appGroupStore: AppGroupStore
var body: some View {
let appGroup = appGroupStore.getBinding(withId: task.appGroupId)
print("Task load") // prints infinitely, CPU 100%
return List {
// ...
NavigationLink(destination: AppGroupDetailView(appGroup: appGroup)) {
Text(appGroup.wrappedValue.name)
}
}
.navigationTitle(task.name)
}
}
AppGroupDetailView (simplified)
struct AppGroupDetailView: View {
@Binding var appGroup: AppGroup
// ...
var body: some View {
List {
ForEach(appGroup.apps, id: \.self) { app in
Text(app)
}
}
.navigationTitle(appGroup.name)
}
}
Symptoms:
- Tapping the
NavigationLink
leads to infinite “Task load” logs and 100% CPU usage. - The
set
closure (“New value set for...”) is never called, so it’s not repeatedly writing. - If I replace the
Binding<AppGroup>
with a read-only approach (just accessing the dictionary), it does not get stuck.
Question:
What might cause SwiftUI to keep re-rendering the body
indefinitely, even if my custom get
closure doesn’t explicitly mutate the state? Are there known pitfalls when using a dictionary-based store and returning a Binding
like this?
Any help is much appreciated!
Thanks in advance for your insights!