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

SwiftUI中登录后变量值无法保存问题求助

SwiftUI登录数据无法持久化:userData在POST请求后变为nil的解决方案

我看到你在SwiftUI应用里遇到了登录数据无法持久化的问题——API返回的数据在POST函数执行时存在,但函数结束后userData就变成nil了。咱们一步步来排查和解决这个问题。

一、先解决视图生命周期导致的数据丢失问题

你在调用代码里用了@ObservedObject var networkManager = NetworkManager(),这很可能是核心问题之一:

  • @ObservedObject的实例会随着视图的重建(比如导航跳转后返回、屏幕旋转)被重新初始化,之前存储的userData自然就丢失了。
  • 优化方案:
    1. 改用@StateObject(iOS14+):
      它能保证对象在视图的整个生命周期内只初始化一次,不会因为视图重建丢失数据:
      @StateObject var networkManager = NetworkManager()
      
    2. 将NetworkManager改为单例:
      确保整个App里只有一个NetworkManager实例,避免重复初始化:
      class NetworkManager: ObservableObject {
          static let shared = NetworkManager() // 全局单例
          private init() {} // 私有化构造方法,防止创建多个实例
      
          @Published var userData : SignInData?
          @Published var signInSuccess: Bool = false
      
          // 你的signIn方法...
      }
      
      调用时直接使用单例:
      @ObservedObject var networkManager = NetworkManager.shared
      

二、实现本地持久化,让数据重启APP后也能保留

上面的方法解决了内存中数据的生命周期问题,但如果用户重启APP,数据还是会消失。我们需要把SignInData保存到本地存储中:

用UserDefaults实现轻量持久化(适合当前数据结构)

因为你的SignInData已经实现了Codable,直接加保存和读取逻辑即可:

class NetworkManager: ObservableObject {
    static let shared = NetworkManager()
    private init() {
        // APP启动时自动读取本地保存的数据
        loadSavedUserData()
    }

    @Published var userData : SignInData?
    @Published var signInSuccess: Bool = false

    // 保存用户数据到UserDefaults
    private func saveUserData() {
        guard let user = userData else { return }
        do {
            let encodedData = try JSONEncoder().encode(user)
            UserDefaults.standard.set(encodedData, forKey: "SavedUserInfo")
        } catch {
            print("保存用户数据失败:\(error)")
        }
    }

    // 从UserDefaults读取用户数据
    private func loadSavedUserData() {
        guard let savedData = UserDefaults.standard.data(forKey: "SavedUserInfo") else { return }
        do {
            userData = try JSONDecoder().decode(SignInData.self, from: savedData)
            signInSuccess = userData != nil // 读取到数据则标记登录状态
        } catch {
            print("读取用户数据失败:\(error)")
        }
    }

    // 修改signIn方法,设置userData后立即保存到本地
    func signIn(bodyData:Any, completion: @escaping((Error?, SignInData?) -> Void)) {
        if let url = URL(string: signInURL) {
            let session = URLSession.shared
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.httpBody = try! JSONSerialization.data(withJSONObject: bodyData)
            let task = session.dataTask(with: request) { (data, response, error) in
                if let err = error {
                    completion(err, nil)
                    return
                }
                guard let safeData = data else {
                    completion(NSError(domain: "DataError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未获取到数据"]), nil)
                    return
                }
                do {
                    let results = try decoder.decode(SignInData.self, from: safeData)
                    DispatchQueue.main.async { // 把sync换成async,避免死锁风险
                        self.userData = results
                        self.saveUserData() // 保存到本地
                        self.signInSuccess = true
                        completion(nil,results)
                    }
                } catch {
                    print(error)
                    completion(error,nil)
                }
            }
            task.resume()
        }
    }
}

注意:如果你的数据包含敏感信息(比如后续会加的Token),建议用Keychain存储,UserDefaults适合存储非敏感的轻量数据。

三、修复DispatchQueue的潜在问题

你原来的代码里用了DispatchQueue.main.sync,如果调用signIn的线程已经是主线程,可能会导致死锁。换成DispatchQueue.main.async更安全,不会阻塞线程。

四、测试验证要点

  1. 登录成功后,检查userData是否被正确赋值,且UserDefaults中能查到SavedUserInfo数据;
  2. 重启APP,看userData是否能被自动读取,不会变成nil;
  3. 导航跳转或旋转屏幕,验证视图重建后userData依然存在。

内容的提问来源于stack exchange,提问作者Benjamin Falkner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:03:12