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

Swift MVVM架构下网络请求成功但数据无法显示问题排查

Swift MVVM应用问题排查与修复

问题现象

开发基于MVVM的Swift应用时,NewsService中每次网络请求成功都会打印success,但ViewModel中articles的didSet闭包、以及fetchNews闭包里的print(articles)都无输出,表格也无法加载数据。

核心排查点与修复方案

1. JSON解码失败但未捕获错误

你的NewsService中,解码失败时直接return,没有打印错误信息,导致无法定位解码问题——这是最可能的原因:即使走到了print("success"),如果JSON结构和Model不匹配,解码会失败,进而不会调用completion(articles),ViewModel的闭包自然不会执行,也就不会有articles的打印输出。

修复代码:
在NewsService的catch块中添加错误打印:

class NewsService {
    static let shared = NewsService()
    
    func fetchNews(completion: @escaping ([Article]) -> Void) {
        // 确保这里填入有效的API URL
        guard let url = URL(string: "你的新闻API地址") else {
            print("无效的URL")
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                print("网络请求错误:\(error)")
                return
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                print("响应状态码错误")
                return
            }
            
            guard let data = data else {
                print("无返回数据")
                return
            }
            
            let decoder = JSONDecoder()
            do {
                print("success")
                let articles = try decoder.decode(Response.self, from: data).articles
                completion(articles)
            } catch {
                // 新增:打印解码错误,定位问题
                print("JSON解码失败:\(error)")
                return
            }
        }
        task.resume()
    }
}

通过打印的错误信息,可以快速定位:比如API返回的description是可选值,但你的Article结构体里定义为非可选let description: String;或者JSON字段名与Model属性名不匹配(比如API返回desc而不是description)。

2. URL无效导致请求未发起

你贴出的代码中URL(string: "")是空字符串,这会返回nil,整个请求逻辑不会执行。但你说会打印success,推测是你贴代码时隐藏了真实URL,务必确保填入的是完整、有效的HTTPS地址。

3. ViewModel与ViewController的联动问题

你在ViewController中设置了newsTableVM.delegate = self,但ViewModel中并未定义delegate属性,也没有在articles更新时通知ViewController刷新UI。即使后续数据加载成功,表格也不会自动刷新。

修复代码:
首先在ViewModel中定义delegate协议:

protocol NewsTableViewModelDelegate: AnyObject {
    func articlesDidUpdate()
}

class NewsTableViewModel {
    weak var delegate: NewsTableViewModelDelegate?
    var articles = [Article]() {
        didSet {
            print(articles)
            // 数据更新时通知ViewController
            delegate?.articlesDidUpdate()
        }
    }
    
    func fetchNews() {
        NewsService.shared.fetchNews { [weak self] articles in
            guard let self = self else { return }
            self.articles = articles
            print(articles)
        }
    }
}

然后在ViewController中实现协议方法,刷新表格:

class NewsTableVC: UITableViewController, NewsTableViewModelDelegate {
    // ... 现有代码
    
    func articlesDidUpdate() {
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        newsTableVM.delegate = self
        newsTableVM.fetchNews()
        // 只做表格注册,不要提前reloadData(此时数据还未返回)
        tableView.register(ArticleCell.self, forCellReuseIdentifier: reuseIdentifier)
    }
    
    // 移除原updateUI方法,或修改为仅做注册逻辑
}

4. 提前调用表格刷新

原ViewController的viewDidLoad中,调用fetchNews()后立即调用updateUI()执行tableView.reloadData(),此时网络请求还未完成,articles是空数组,刷新操作无意义,建议移除这部分提前刷新的逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 15:20:26