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

iOS UIKit网络数据无法显示在TableView单元格的问题求助

iOS UIKit TableView无数据显示问题排查与修复

问题描述

在iOS UIKit开发中,从网络拉取的数据打印显示为[ProjectName.modelName (userId: id: , title: , body: )],将数据放入TableView单元格时无法显示。已将数据存入数组,但打印数组长度始终为0,单元格无内容。

相关代码

网络请求方法

func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void){
    let url = URL(string:"https://jsonplaceholder.typicode.com/posts")!
    
    URLSession.shared.dataTask(with: url) { data, res, error in
        guard let data else{ return }
        
        do{
            let posts = try JSONDecoder().decode([Posts].self, from: data)
            completion(.success(posts))
            print(posts)
            
        }catch{
            completion(.failure(NSError()))
        }
    }.resume()
}

接口返回JSON格式

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }, ...
]

协议定义

protocol UserViewModelOutput: AnyObject{
    func updateView(title:[Posts])
}


protocol PostsService{
    func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void)
}

TableViewController代码

class ViewController: UIViewController, UserViewModelOutput {
    
    private let viewModel: PostViewModel
    var postsArray = [Posts]()
    
    lazy var homeTableView: UITableView = {
        let tableView = UITableView()
        
        tableView.delegate = self
        tableView.dataSource = self
        
        tableView.register(HomeTableViewCell.self, forCellReuseIdentifier: HomeTableViewCell.reuseIdentifier)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        return tableView
    }()
    
    init(viewModel: PostViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
        self.viewModel.output = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        fetchUsers()
        view.backgroundColor = .yellow
    }
    
    private func setupViews() {
        NSLayoutConstraint.activate([
            homeTableView.topAnchor.constraint(equalTo: view.topAnchor),
            homeTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            homeTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            homeTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    func fetchUsers(){
        viewModel.fetchPost()
    }
    
    func updateView(title: [Posts]) {
        self.postsArray = title
    }
    
}

extension ViewController:UITableViewDataSource, UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print(postsArray.count)
        return postsArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = homeTableView.dequeueReusableCell(withIdentifier: HomeTableViewCell.reuseIdentifier, for: indexPath)
        cell.textLabel?.text = self.postsArray[indexPath.row].title
        
        return cell
    }
    
}

错误排查与修复方案

1. 数据更新后未刷新TableView

问题:updateView方法仅给postsArray赋值,但未通知TableView重新加载数据,导致TableView不知道数据源已更新,仍显示旧的空数组状态。

修复:在updateView方法中添加TableView刷新代码,同时确保在主线程执行(网络回调默认在后台线程,UI操作必须在主线程):

func updateView(title: [Posts]) {
    DispatchQueue.main.async {
        self.postsArray = title
        self.homeTableView.reloadData()
    }
}

2. Posts模型未正确实现Codable或属性名不匹配

问题:打印数据显示[ProjectName.modelName (userId: id: , title: , body: )],说明模型属性可能未正确映射JSON字段,或未实现CustomStringConvertible导致打印信息不完整,甚至可能解码失败但未捕获错误。

修复:

  • 确保Posts模型正确实现Codable,属性名与JSON字段完全一致:
struct Posts: Codable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}
  • 可选:实现CustomStringConvertible以便更清晰打印模型数据:
extension Posts: CustomStringConvertible {
    var description: String {
        return "Posts(userId: \(userId), id: \(id), title: \(title), body: \(body))"
    }
}

3. ViewModel的fetchPost方法未正确调用服务并回调

问题:未提供PostViewModel代码,若fetchPost方法未正确调用PostsService的fetchPosts,或未在回调中调用output?.updateView(title:),则数据无法传递到ViewController。

修复:确保PostViewModel的fetchPost方法正确实现:

class PostViewModel {
    weak var output: UserViewModelOutput?
    private let service: PostsService
    
    init(service: PostsService) {
        self.service = service
    }
    
    func fetchPost() {
        service.fetchPosts { [weak self] result in
            guard let self = self else { return }
            switch result {
            case .success(let posts):
                self.output?.updateView(title: posts)
            case .failure(let error):
                print("请求失败:\(error.localizedDescription)")
            }
        }
    }
}

4. 网络请求错误处理不完善

问题:当前网络请求中,guard let data else{ return }直接返回未处理错误;catch块返回空的NSError(),无法定位解码失败原因。

修复:完善错误处理,在无数据时通过completion返回错误,同时捕获具体解码错误:

func fetchPosts(completion: @escaping (Result<[Posts],Error>) -> Void){
    guard let url = URL(string:"https://jsonplaceholder.typicode.com/posts") else {
        completion(.failure(NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的URL"])))
        return
    }
    
    URLSession.shared.dataTask(with: url) { data, res, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        
        guard let data = data else {
            completion(.failure(NSError(domain: "NoData", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到数据"])))
            return
        }
        
        do{
            let posts = try JSONDecoder().decode([Posts].self, from: data)
            completion(.success(posts))
            print(posts)
            
        }catch let decodingError {
            completion(.failure(decodingError))
            print("解码失败:\(decodingError.localizedDescription)")
        }
    }.resume()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:45:57