Swift:JSON解码为自定义结构体并填充Table View问题排查
Swift TableView 填充API数据问题修复与改进建议
问题背景
我有自定义结构体作为数据模型,API返回JSON字典,需要将字典值填充到TableView单元格中。已创建包含键名的数组用于设置ViewController中标签内容,单元格包含文本框和标签,希望对应显示API返回的数据,但尝试解析为数组后没有任何数据加载,求改进建议。
核心问题分析
- API接口返回单个用户对象,而非用户数组,原代码用
[UserData].self解析会直接失败,导致无数据 - TableView行数逻辑错误:原代码返回数组长度,但实际需求是每个用户属性对应一行,应返回属性标题数组的长度
- 单元格赋值逻辑错误:仅固定显示
first_name,未根据每行属性标题匹配对应数据 - 变量命名不符合Swift规范(如
DataTable、UserArr应使用小驼峰命名) - 未处理嵌套结构(如
employment、address)和可选类型(如subscription)的展示
修复与改进步骤
- 修正API解析逻辑:将解析目标改为单个
UserData对象,调整回调类型与数据存储变量 - 调整TableView数据源配置:行数改为属性标题数组长度,按行索引匹配标题与对应数据
- 实现数据属性动态映射:给
UserData添加方法,根据属性标题返回对应字符串值,处理嵌套结构与可选值 - 规范变量命名:遵循Swift小驼峰规则,提升代码可读性
- 优化嵌套数据展示:将嵌套结构整理为可读字符串,处理可选值空值情况
修改后的代码实现
ViewController 代码
import UIKit class FirstViewController: UIViewController { @IBOutlet weak var dataTable: UITableView! // 属性标题数组,对应每个单元格的标签文本 let userPropertyTitles = ["ID", "UID", "Password", "First Name", "Last Name", "Username", "Email", "Avatar", "Gender", "Phone Number", "Social Insurance Number", "Date Of Birth", "Employment", "Address", "Zip Code", "State", "Country", "Coordinates", "CC Number", "Subscription"] // 存储单个用户数据(API返回单个对象) var currentUser: UserData? override func viewDidLoad() { super.viewDidLoad() dataTable.dataSource = self dataTable.delegate = self NetworkManager.shared.fetchData { [weak self] userData in self?.currentUser = userData DispatchQueue.main.async { self?.dataTable.reloadData() } } } } extension FirstViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 每个属性对应一行,返回标题数组的长度 return userPropertyTitles.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DataTableViewCell let title = userPropertyTitles[indexPath.row] cell.txtLabel.text = title // 根据标题获取对应的数据值,空值显示N/A cell.txtField.text = currentUser?.value(forPropertyTitle: title) ?? "N/A" return cell } }
UserData 模型代码
import Foundation struct UserData: Codable { let id: Int let uid: String let password: String let first_name: String let last_name: String let username: String let email: String let avatar: String let gender: String let phone_number: String let social_insurance_number: String let date_of_birth: String let employment: Employment let address: Address let credit_card: CreditCard let subscription: Subscription? struct Employment: Codable { let title: String let key_skill: String } struct Address: Codable { let city: String let street_name: String let street_address: String let zip_code: String let state: String let country: String let coordinates: Coordinates struct Coordinates: Codable { let lat: Double let lng: Double } } struct CreditCard: Codable { let cc_number: String } struct Subscription: Codable { let plan: String let status: String let payment_method: String let term: String } // 根据属性标题返回对应的值字符串 func value(forPropertyTitle title: String) -> String { switch title { case "ID": return String(id) case "UID": return uid case "Password": return password case "First Name": return first_name case "Last Name": return last_name case "Username": return username case "Email": return email case "Avatar": return avatar case "Gender": return gender case "Phone Number": return phone_number case "Social Insurance Number": return social_insurance_number case "Date Of Birth": return date_of_birth case "Employment": return "\(employment.title) - \(employment.key_skill)" case "Address": return "\(address.street_address), \(address.street_name), \(address.city)" case "Zip Code": return address.zip_code case "State": return address.state case "Country": return address.country case "Coordinates": return "Lat: \(address.coordinates.lat), Lng: \(address.coordinates.lng)" case "CC Number": return credit_card.cc_number case "Subscription": guard let sub = subscription else { return "No subscription" } return "\(sub.plan) (\(sub.status))" default: return "N/A" } } }
NetworkManager 代码
import Foundation import Alamofire class NetworkManager { static let shared = NetworkManager() private init() {} func fetchData(handler: @escaping (_ apiData: UserData) -> Void) { guard let url = URL(string: "https://random-data-api.com/api/v2/users") else { print("Invalid URL") return } // 使用Alamofire自带的Decodable解析,简化代码 AF.request(url) .responseDecodable(of: UserData.self) { response in switch response.result { case .success(let userData): handler(userData) case .failure(let error): print("API Request Error: \(error.localizedDescription)") } } } }
内容的提问来源于stack exchange,提问作者bumblebee
相关产品推荐
相关产品推荐

