You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI接入WatchConnectivity后启动崩溃,涉及AppStorage、ForEach等无关组件

根本原因

该崩溃是多重因素叠加导致的SwiftUI视图重入异常:

  1. Connectivity类初始化时直接激活WCSession,激活回调会在主线程更新@Published属性触发ContentView刷新
  2. 传给fullScreenCover(item:)的是仅实现get方法、set逻辑为空的自定义Binding,该Binding依赖@AppStorage属性,当@Published更新和@AppStorage初始化读取两个事件同时触发视图body计算时就会产生竞态
  3. 使用枚举的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 07:45:03