SwiftUI应用快速切换页面时崩溃问题排查(报错:saw PreferenceKeys, expected ViewList)
Looks like you're hitting a classic SwiftUI AttributeGraph mismatch issue that's common when dealing with navigation state and view tree updates. The error invalid value type for attribute: 632448 (saw PreferenceKeys, expected ViewList) points to SwiftUI failing to reconcile the view tree structure when returning to your home screen. Let's break down the root causes and fix them step by step:
1. Fix Unstable Navigation State
Your @State var page:pages! uses an implicitly unwrapped optional, which creates state instability. SwiftUI can't properly handle sudden nil-to-value transitions with this setup, leading to view tree mismatches.
Fix:
Replace the implicitly unwrapped optional with a regular optional:
@State var page:pages? // Remove the !
This lets SwiftUI properly manage the navigation state, and your navigation links will automatically clean up when page is set back to nil (when returning home).
2. Fix ForEach View Reuse Issues
Using array indices as the id in your ForEach is risky—when your cats array changes, SwiftUI will reuse views incorrectly, leading to AttributeGraph conflicts.
Fix:
Traverse your filtered cats array directly and use a unique property from CAT_BITE (like type) as the identifier:
// Replace your existing LazyVGrid ForEach LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())] ,spacing: 8){ ForEach(env.cats.filter { $0.type != "DAILY_FEED" }, id: \.type) { cat in Button(action: { page = pages.withLabel(cat.type) }) { catBite( i: env.cats.firstIndex(where: { $0.type == cat.type }) ?? 0, dto: cat ) .environmentObject(env) } } }
3. Remove Unnecessary Main Queue Wraps
SwiftUI's onChange modifier already runs on the main thread—adding an extra DispatchQueue.main.async creates delayed state updates that can desync the view tree.
Fix:
Simplify your onChange handler:
.onChange(of: env.open_page) { newPage in page = newPage // No need for DispatchQueue.main.async here }
4. Replace Static Animation State with @State
Your static w_landing.isAnimated variable doesn't trigger view updates when changed. SwiftUI ignores static properties for view invalidation.
Fix:
Convert it to a @State property:
@State private var isAnimated = false // Replace static variable // Update your onAppear handler .onAppear(){ DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) { self.isAnimated = true } // ... rest of your code }
5. Fix Timer Lifecycle Management
Using a Timer.publish directly in your view can lead to duplicate timers when the view rebuilds, causing unexpected state changes.
Fix:
Use an ObservableObject to manage the timer lifecycle:
// Add this helper class outside your view class TimerManager: ObservableObject { @Published var seconds = 0 private var timer: Timer? init() { timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in guard let self = self else { return } self.seconds += 1 } } deinit { timer?.invalidate() } } // In your view, replace the timer with: @StateObject private var timerManager = TimerManager() // Update your onReceive handler .onReceive(timerManager.$seconds) { seconds in env.seconds = seconds }
6. Use @EnvironmentObject Consistently
You're mixing @EnvironmentObject var env:Env with direct access to Env.sharedInstance. This breaks SwiftUI's data flow, as changes to the singleton won't trigger view updates.
Fix:
Replace all Env.sharedInstance calls with your env property:
// Example: Replace Env.sharedInstance.hasNotch .padding(.top, env.hasNotch ? 80 : 50) // Update your rebuild() function fileprivate func rebuild(){ if env.cats.count > 0 { return } if let _cats:[Any] = env.checkin?.payload!["categories"] as? [Any] { env.cats = [] for cat in _cats { guard let cat = cat as? [String:Any] else { continue } env.cats.append(CAT_BITE(cat)) } self.daily_feed = env.cats.first(where: { $0.type == "DAILY_FEED" }) } }
7. Remove Unnecessary Hardcoded IDs
Hardcoded .id("landing") and .id("the_main_list") force SwiftUI to rebuild entire view sections unnecessarily, which can trigger AttributeGraph conflicts. Let SwiftUI manage view identities automatically unless you explicitly need a full rebuild.
Final Notes
These changes will stabilize your view tree by:
- Ensuring state changes trigger proper view updates
- Letting SwiftUI correctly reuse and reconcile views
- Fixing navigation state management
- Aligning with SwiftUI's data flow principles
After implementing these fixes, upload a new TestFlight build to verify the crash is resolved.
内容的提问来源于stack exchange,提问作者Kodr.F

