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

SwiftUI中ObservableObject跨实例修改属性的问题求助

解决SwiftUI中两个ObservableObject的交互问题

错误原因分析

  1. 第一种错误:直接在LoginViewModel中实例化AppSettings会创建全新的独立实例,和MyApp中全局的@StateObject实例不是同一个,修改该实例的isLogin状态不会同步到ContentView的判断逻辑。
  2. 第二种错误:@EnvironmentObject只能用于View结构体,无法直接在ObservableObject类中使用,因此无法通过该方式获取全局的AppSettings实例。

解决方案:依赖注入传递AppSettings

通过将全局的AppSettings实例注入到LoginViewModel中,实现两个ObservableObject的状态同步。

1. 修改LoginViewModel,添加初始化参数接收AppSettings

class LoginViewModel: ObservableObject {
    
    @Published var login = ""
    @Published var password = ""
    
    // 持有全局的AppSettings实例
    private let appSettings: AppSettings
    
    // 初始化时传入AppSettings
    init(appSettings: AppSettings) {
        self.appSettings = appSettings
    }
    
    func auth() {
        UserAPI().Auth(req: LoginRequest(email: login, password: password)) { response, error in
            if let err = error {
                // 错误处理逻辑
            } else if let response = response {
                Defaults.accessToken = response.tokens.accessToken
                Defaults.refreshToken = response.tokens.refreshToken
                // 切换到主线程更新状态(网络请求在后台线程)
                DispatchQueue.main.async {
                    self.appSettings.isLogin = true
                }
            }
        }
    }
}

2. 修改LoginView,注入AppSettings并初始化ViewModel

struct LoginView: View {
    @EnvironmentObject var appSettings: AppSettings
    @StateObject var vm: LoginViewModel
    
    // 自定义初始化方法,接收AppSettings实例
    init(appSettings: AppSettings) {
        _vm = StateObject(wrappedValue: LoginViewModel(appSettings: appSettings))
    }
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Email", text: $vm.login)
                    .textFieldStyle(.roundedBorder)
                    .padding()
                TextField("Password", text: $vm.password)
                    .textFieldStyle(.roundedBorder)
                    .padding()
                    .secureFieldStyle()
                
                Button {
                    vm.auth()
                } label: {
                    Text("SignIn")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
            }
            .padding()
        }
    }
}

3. 修改ContentView,传递AppSettings到LoginView

struct ContentView: View {
    @EnvironmentObject var vm: AppSettings
    
    var body: some View {
        if vm.isLogin {
            MainView(appSettings: vm)
                .environmentObject(vm)
        } else {
            // 将全局的AppSettings实例传入LoginView
            LoginView(appSettings: vm)
                .environmentObject(vm)
        }
    }
}

登出功能实现(同理)

在主页面的ViewModel中同样通过依赖注入获取AppSettings,修改isLogin状态即可:

class MainViewModel: ObservableObject {
    private let appSettings: AppSettings
    
    init(appSettings: AppSettings) {
        self.appSettings = appSettings
    }
    
    func logout() {
        // 清除token
        Defaults.accessToken = nil
        Defaults.refreshToken = nil
        // 切换到主线程更新状态
        DispatchQueue.main.async {
            self.appSettings.isLogin = false
        }
    }
}

struct MainView: View {
    @EnvironmentObject var appSettings: AppSettings
    @StateObject var vm: MainViewModel
    
    init(appSettings: AppSettings) {
        _vm = StateObject(wrappedValue: MainViewModel(appSettings: appSettings))
    }
    
    var body: some View {
        TabView {
            Text("Home")
                .tabItem {
                    Image(systemName: "house")
                    Text("Home")
                }
            Button("Logout") {
                vm.logout()
            }
            .tabItem {
                Image(systemName: "person.crop.circle")
                Text("Profile")
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Ilia Ivanchik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:45:31