SwiftUI中如何在不同模型之间共享数据?
报错原因
Swift 结构体初始化有严格的安全检查规则:所有存储属性必须在访问self的任何属性或方法之前完成赋值。你的代码中@EnvironmentObject修饰的user属于HomeView的实例属性,你在init方法中访问user的时候,alerts还没有完成初始化,自然就触发了'self' used before all stored properties are initialized的报错。
推荐解决方案
方案1:拆分Alerts的初始化和数据请求逻辑(最符合SwiftUI设计规范)
把Alerts的实例初始化和网络请求逻辑拆分,不需要修改现有视图层级:
- 给Alerts添加无参默认初始化方法,把拉取服务端数据的逻辑封装为单独的
fetchData(userId:)方法 - 把
alerts改为@StateObject(iOS 14+推荐,视图内部持有的可观察对象用@StateObject可以避免视图刷新时重复初始化的问题) - 在
HomeView的onAppear回调中判断user.id是否存在,存在则调用alerts的拉取方法
代码示例:
struct HomeView: View { @EnvironmentObject var user: User // 先完成空初始化,满足Swift的初始化规则 @StateObject var alerts = Alerts() var body: some View { // 你的HomeView原有布局代码 VStack { // 展示提醒列表等逻辑 } .onAppear { guard let userId = user.id else { return } alerts.fetchData(userId: userId) } } } // Alerts模型修改示例 class Alerts: ObservableObject { @Published var alertList: [AlertItem] = [] func fetchData(userId: String) { // 原有请求逻辑挪到此处,用传入的userId发起网络请求 } }
方案2:父视图提前初始化Alerts传入
在跳转进入HomeView的父视图中,用户已经登录、user.id已经存在的时机,提前初始化Alerts实例,作为参数直接传入HomeView:
父视图跳转逻辑示例:
// 父视图中已经持有user的EnvironmentObject if user.isLoginValid { // 提前初始化Alerts传入 HomeView(alerts: Alerts(userId: user.id!)) .environmentObject(user) }
HomeView代码可以直接简化,不需要自定义初始化方法:
struct HomeView: View { @EnvironmentObject var user: User @ObservedObject var alerts: Alerts // 直接使用Swift自动生成的构造器即可 }
方案3:将Alerts也升级为全局共享对象
如果Alerts的数据需要在多个视图中复用,可以直接把Alerts也设置为EnvironmentObject,在用户登录完成、拿到userId的时机完成Alerts的初始化,注入到整个视图树中,后续所有视图都可以直接读取使用,不需要在HomeView单独处理。
关于嵌套视图方案的说明
你找到的嵌套视图方案不属于违规实现,本质是利用body闭包执行时、视图已经完全初始化、环境变量已经加载完成的时机传参初始化子视图,但是如果Alerts仅在HomeView中使用,上面三个方案的代码可读性和维护性会更高。
内容的提问来源于stack exchange,提问作者Biiiiiird
相关产品推荐
相关产品推荐

