iOS开发:如何在两个ViewModel间传递数据?
问题分析
你遇到的核心问题是:GetTokenViewModel内部初始化了一个全新的UserPostViewModel实例,这个实例和你实际调用createUser()的那个不是同一个对象。即使外部的user已经获取到数据,内部的userVM.user依然是nil,导致拼接URL时出错。
解决方案
下面提供几种常用的解决方式,按推荐程度排序:
方案一:依赖注入(最推荐)
直接把已初始化并完成数据请求的UserPostViewModel实例传入GetTokenViewModel,避免创建新实例。
修改GetTokenViewModel
class GetTokenViewModel: ObservableObject { @Published var tokenData: DataToken? let userVM: UserPostViewModel // 改为外部传入的实例 // 初始化时注入已有的UserPostViewModel init(userVM: UserPostViewModel) { self.userVM = userVM } func fetchData() { // 先校验用户token是否存在 guard let userToken = userVM.user?.token else { print("用户token未获取到") return } let urlString = "privateapi/\(userToken)" guard let url = URL(string: urlString) else { print("URL格式错误") return } var request = URLRequest(url:url) URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data else { print("请求失败或无返回数据: \(error?.localizedDescription ?? "未知错误")") return } do { let decodedData = try JSONDecoder().decode(ResponseToken.self, from: data) DispatchQueue.main.async{ self.tokenData = decodedData.data print("TOKEN: \(self.tokenData?.token)") } } catch { print("JSON解码错误: \(error)") } }.resume() } }
使用方式
// 先创建并初始化UserPostViewModel,完成用户数据请求 let userVM = UserPostViewModel() userVM.createUser() // 确认user非空后,初始化GetTokenViewModel并传入实例 // 可以通过监听user的变化,或者在回调里触发 if userVM.user != nil { let tokenVM = GetTokenViewModel(userVM: userVM) tokenVM.fetchData() }
方案二:自动监听用户数据变化(Combine)
如果需要在用户数据获取完成后自动请求token,可以用Combine监听UserPostViewModel的user属性变化。
import Combine class GetTokenViewModel: ObservableObject { @Published var tokenData: DataToken? let userVM: UserPostViewModel private var cancellables = Set<AnyCancellable>() init(userVM: UserPostViewModel) { self.userVM = userVM // 监听user变化,当token存在时自动请求 $userVM.user .compactMap { $0?.token } .sink { [weak self] token in self?.fetchData(with: token) } .store(in: &cancellables) } // 重载方法,直接接收token参数 func fetchData(with token: String) { let urlString = "privateapi/\(token)" guard let url = URL(string: urlString) else { print("URL格式错误") return } var request = URLRequest(url:url) URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data else { print("请求失败或无返回数据: \(error?.localizedDescription ?? "未知错误")") return } do { let decodedData = try JSONDecoder().decode(ResponseToken.self, from: data) DispatchQueue.main.async{ self.tokenData = decodedData.data print("TOKEN: \(self.tokenData?.token)") } } catch { print("JSON解码错误: \(error)") } }.resume() } }
方案三:手动传递token
如果不需要关联整个ViewModel,也可以直接把token作为参数传入fetchData方法。
修改GetTokenViewModel
class GetTokenViewModel: ObservableObject { @Published var tokenData: DataToken? func fetchData(with token: String) { let urlString = "privateapi/\(token)" guard let url = URL(string: urlString) else { print("URL格式错误") return } var request = URLRequest(url:url) URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data else { print("请求失败或无返回数据: \(error?.localizedDescription ?? "未知错误")") return } do { let decodedData = try JSONDecoder().decode(ResponseToken.self, from: data) DispatchQueue.main.async{ self.tokenData = decodedData.data print("TOKEN: \(self.tokenData?.token)") } } catch { print("JSON解码错误: \(error)") } }.resume() } }
使用方式
let userVM = UserPostViewModel() userVM.createUser() // 确认token存在后调用 if let token = userVM.user?.token { let tokenVM = GetTokenViewModel() tokenVM.fetchData(with: token) }
额外优化建议
- 你的
createUser()方法中用了try? JSONDecoder().decode(...),会吞掉解码错误,建议改成try并捕获错误,方便调试。 - 网络请求完成后一定要切回主线程更新UI,你已经做了这一点,保持即可。
- 所有网络请求前都要做参数校验,避免无效请求。
内容的提问来源于stack exchange,提问作者Luka Šalipur
相关产品推荐
相关产品推荐

