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

Swift MVVM模式下网络请求后ViewController数据为nil问题排查

Swift MVVM异步网络请求导致数据未及时加载的问题

问题核心原因

网络请求是异步操作:postGenericCallVM方法调用后会立即返回,后台才会执行实际的网络请求逻辑,而你在ViewController里调用该方法后立刻打印postDataVM的属性,此时请求还没完成,postDataVM仍处于初始的nil状态,自然打印出nil。

解决方案

下面提供几种常用的解决方式,根据项目需求选择即可:

1. 闭包回调通知数据更新

修改ViewModel的方法,添加完成回调,在请求结束后通知ViewController:

class PostViewModel {
    var postDataVM: PostGenResult?
    
    func postGenericCallVM(completion: @escaping (PostGenResult?, Error?) -> Void) {
        // 构造你的请求对象
        let request = RequestObject(
            method: .get, // 替换为你的请求方法
            urlPath: "your/api/path", // 替换为你的接口路径
            isTokenNeeded: true
        )
        
        APINetworkManager2.sharedInstance.serviceCall(requestObject: request) { [weak self] result in
            switch result {
            case .success(let response):
                self?.postDataVM = response.data // 假设APIResponse包含data字段
                completion(self?.postDataVM, nil)
            case .failure(let error):
                completion(nil, error)
            }
        }
    }
}

ViewController中通过回调获取更新后的数据:

class ViewController: UIViewController {
    private let postVM = PostViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        postVM.postGenericCallVM { [weak self] data, error in
            guard let self = self else { return }
            
            if let error = error {
                print("请求失败:\(error.localizedDescription)")
                return
            }
            
            // 此时postDataVM已经被赋值,可以正常访问
            print("testing viewmodel data in viewcontroller \(self.postVM.postDataVM?.postCount)")
            // 这里执行UI更新操作,比如刷新列表
        }
    }
}

2. 使用Combine框架(iOS 13+)

通过@Published属性发布数据变化,ViewController订阅该属性的更新:

import Combine

class PostViewModel {
    @Published var postDataVM: PostGenResult?
    private var cancellables = Set<AnyCancellable>()
    
    func postGenericCallVM(){
        let request = RequestObject(
            method: .get,
            urlPath: "your/api/path",
            isTokenNeeded: true
        )
        
        APINetworkManager2.sharedInstance.serviceCall(requestObject: request)
            .receive(on: DispatchQueue.main) // 切换到主线程更新UI
            .sink(receiveCompletion: { completion in
                if case .failure(let error) = completion {
                    print("请求失败:\(error.localizedDescription)")
                }
            }, receiveValue: { [weak self] response in
                self?.postDataVM = response.data
            })
            .store(in: &cancellables)
    }
}

ViewController中订阅数据变化:

import Combine

class ViewController: UIViewController {
    private let postVM = PostViewModel()
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        postVM.postGenericCallVM()
        
        // 订阅postDataVM的变化
        postVM.$postDataVM
            .compactMap { $0 } // 过滤掉nil值
            .sink { data in
                print("testing viewmodel data in viewcontroller \(data.postCount)")
                // 执行UI更新
            }
            .store(in: &cancellables)
    }
}

3. Delegate模式

通过协议定义回调方法,让ViewController作为ViewModel的代理,接收数据更新通知:

// 定义代理协议
protocol PostViewModelDelegate: AnyObject {
    func postDataDidUpdate(_ data: PostGenResult)
    func postDataRequestFailed(_ error: Error)
}

class PostViewModel {
    weak var delegate: PostViewModelDelegate?
    var postDataVM: PostGenResult?
    
    func postGenericCallVM(){
        let request = RequestObject(
            method: .get,
            urlPath: "your/api/path",
            isTokenNeeded: true
        )
        
        APINetworkManager2.sharedInstance.serviceCall(requestObject: request) { [weak self] result in
            guard let self = self else { return }
            
            switch result {
            case .success(let response):
                self.postDataVM = response.data
                self.delegate?.postDataDidUpdate(self.postDataVM!)
            case .failure(let error):
                self.delegate?.postDataRequestFailed(error)
            }
        }
    }
}

ViewController实现代理协议:

class ViewController: UIViewController, PostViewModelDelegate {
    private let postVM = PostViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        postVM.delegate = self
        postVM.postGenericCallVM()
    }
    
    func postDataDidUpdate(_ data: PostGenResult) {
        print("testing viewmodel data in viewcontroller \(data.postCount)")
        // 执行UI更新
    }
    
    func postDataRequestFailed(_ error: Error) {
        print("请求失败:\(error.localizedDescription)")
    }
}

注意事项

  • 处理网络请求回调时,必须使用[weak self]避免循环引用,防止内存泄漏。
  • 所有UI更新操作都要在主线程执行,Combine的receive(on: DispatchQueue.main)或者手动调用DispatchQueue.main.async都可以实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 01:46:19