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

