SwiftUI接入WatchConnectivity后启动崩溃,涉及AppStorage、ForEach等无关组件
根本原因
该崩溃是多重因素叠加导致的SwiftUI视图重入异常:
Connectivity类初始化时直接激活WCSession,激活回调会在主线程更新@Published属性触发ContentView刷新- 传给
fullScreenCover(item:)的是仅实现get方法、set逻辑为空的自定义Binding,该Binding依赖@AppStorage属性,当@Published更新和@AppStorage初始化读取两个事件同时触发视图body计算时就会产生竞态 - 使用枚举的
hashValue作为Identifiable的id,Swift运行时在特定布局下会出现哈希值不稳定的情况,进一步加重了状态错乱概率
你提到的无关修改本质上都是改变了视图的刷新时机,刚好避开了竞态窗口,所以崩溃会暂时消失。
修复方案
- 将WCSession的激活逻辑从
Connectivity的init方法移到独立的启动方法中,等视图加载完成后再调用 - 给
ActiveSheet枚举设置固定rawValue作为id,替换动态的hashValue - 替换自定义只读Binding为正常的
@State变量管理弹窗状态,避免状态不同步
修改后可运行代码
import SwiftUI import WatchConnectivity struct ContentView: View { @StateObject var connectivity = Connectivity() @AppStorage("setupKey") var hasBeenSetupBefore: Bool = false // 新增状态变量管理弹窗 @State private var activeSheet: ActiveSheet? var body: some View { Text("Hello World") .fullScreenCover(item: $activeSheet, content: { _ in WelcomeScreen() }) .onAppear { // 视图加载完成后再启动WCSession,避开初始化阶段的状态冲突 connectivity.start() if !hasBeenSetupBefore { activeSheet = .setup } } } enum ActiveSheet: Identifiable { case setup, settings // 用固定值替代hashValue var id: Int { switch self { case .setup: return 0 case .settings: return 1 } } } } struct WelcomeScreen: View { var body: some View { NavigationView { VStack { ForEach(features) { feature in Text("hello") .frame(maxWidth: .infinity, alignment: .leading) } } } } let features = [Feature()] } struct Feature: Identifiable { var id = UUID() } class Connectivity: NSObject, ObservableObject, WCSessionDelegate { @Published var activationState = WCSessionActivationState.notActivated override init() { super.init() } // 独立的启动方法,避免初始化时触发状态更新 func start() { if WCSession.isSupported() { let session = WCSession.default session.delegate = self session.activate() } } func session( _ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error? ) { DispatchQueue.main.async { self.activationState = activationState } } func sessionDidBecomeInactive(_ session: WCSession) {} func sessionDidDeactivate(_ session: WCSession) {} func updateComplication(with data: [String: Any]) {} func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {} }
内容的提问来源于stack exchange,提问作者Isaak
相关产品推荐
相关产品推荐

