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

Swift 如何解决Alpha Vantage接口调用后ViewModel无法接收异步响应的问题

问题根因

companyOverviewRequest的didSet只会在「该属性存储的对象引用被替换」时触发,完全无法感知被引用对象的内部属性变化。
你在fetchCompanyOverview中对self.companyOverviewRequest = request的赋值是无效的:callApi()返回的就是当前请求对象本身,引用没有变化,自然不会触发didSet。等异步网络请求完成、AlphaVantageRequest内部的responseJSON被赋值时,ViewModel没有任何订阅机制,自然收不到数据。

对应解决方案

按改动量从小到大排列:

方案1:在请求链路中添加完成闭包

  1. 给ApiRequest协议补充回调属性:
protocol ApiRequest {
    // 保留原有协议定义
    var onResponseReceived: (([String: Any]?) -> Void)? { get set }
}
  1. 在AlphaVantageRequest的responseJSON观察器中触发回调:
class AlphaVantageRequest : ApiRequest {
    // ... 原有实现
    var onResponseReceived: (([String: Any]?) -> Void)?
    
    var responseJSON : [String : Any]? {
        didSet {
            guard let results = responseJSON else { return }
            print(results)
            onResponseReceived?(results)
        }
    }
}
  1. 在ViewModel中绑定回调,替换原本无效的didSet:
class CompanyViewModel: ObservableObject {
    // 用@Published直接暴露接口数据给视图
    @Published var companyOverview: [String: Any]?
    
    private var companyOverviewRequest: ApiRequest?
    private var searchEndpoint: SearchEndpoint
    
    init(searchEndpoint: SearchEndpoint) {
        self.searchEndpoint = searchEndpoint
        // 初始化请求时绑定回调
        let request = CompanyOverviewRequest(symbol: searchEndpoint.symbol)
        request.onResponseReceived = { [weak self] response in
            // 切回主线程更新UI状态
            DispatchQueue.main.async {
                self?.companyOverview = response
            }
        }
        self.companyOverviewRequest = request
    }
    
    func fetchCompanyOverview() {
        companyOverviewRequest?.callApi()
    }
}

方案2:用Combine订阅属性变化,贴合现有ObservableObject设计

  1. 将AlphaVantageRequest的responseJSON标记为可发布属性:
class AlphaVantageRequest : ApiRequest {
    // ... 原有实现
    @Published var responseJSON: [String: Any]?
}
  1. 在ViewModel中订阅该属性的变化,替代无效的didSet:
import Combine

class CompanyViewModel: ObservableObject {
    @Published var companyOverview: [String: Any]?
    private var cancellable: AnyCancellable?
    private var companyOverviewRequest: AlphaVantageRequest?
    private var searchEndpoint: SearchEndpoint
    
    init(searchEndpoint: SearchEndpoint) {
        self.searchEndpoint = searchEndpoint
        let request = CompanyOverviewRequest(symbol: searchEndpoint.symbol)
        self.companyOverviewRequest = request
        // 订阅请求对象的响应变化
        cancellable = request.$responseJSON
            .receive(on: DispatchQueue.main)
            .sink { [weak self] response in
                self?.companyOverview = response
            }
    }
    
    func fetchCompanyOverview() {
        companyOverviewRequest?.callApi()
    }
}

额外注意点

你之前在fetchCompanyOverview里写的self.companyOverviewRequest = request是完全多余的:因为callApi()返回的就是当前的同一个请求对象,没有引用变化,赋值后不会触发任何逻辑,直接删掉即可。

网络请求的回调运行在子线程,如果你要将响应数据赋值给@Published属性直接驱动UI,必须切回主线程再更新,否则会出现UI刷新异常的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 08:54:03