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

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)
}
额外优化建议
  1. 你的createUser()方法中用了try? JSONDecoder().decode(...),会吞掉解码错误,建议改成try并捕获错误,方便调试。
  2. 网络请求完成后一定要切回主线程更新UI,你已经做了这一点,保持即可。
  3. 所有网络请求前都要做参数校验,避免无效请求。

内容的提问来源于stack exchange,提问作者Luka Šalipur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:25:43