SwiftUI中如何从二维数组类型API向视图传递数据?
二维数组JSON解析与MVVM数据绑定问题
我在JSON中存储了二维数组类型的数据,想用MVVM模式,借助QuickType生成的Model在ViewModel中获取数据,但多次尝试都失败了。以下是相关代码和数据,求解决办法。
JSON数据
{ "publisherPlans": [[ "Name", "Free","Prime" ],["Book Sell",9999,9999],["Book Bulk Sell",0,9999],["Magazine start-up",9999,9999],["Demo book request count for School",5,9999],["Demo book request Acception",9999,9999],["Assign book for demo",25,9999]]}
Model代码
public struct SchoolPlanModel: Decodable { public let publisherPlans: [[PublisherPlan]] } public enum PublisherPlan: Decodable { case integer(Int) case string(String) }
ViewModel代码
class ReadData: ObservableObject { @Published var datas = [String]() func getData() async { guard let url = URL(string: "********api/plans") else { return } do { let (data, _) = try await URLSession.shared.data(from: url) Task{@MainActor in let results = try JSONDecoder().decode(SchoolPlanModel.self, from: data).publisherPlans print(results) } } catch { print("---> error: \(error)") } } }
View代码
struct SchoolPlanView: View{ @StateObject var list = ReadData() var body: some View{ ForEach(list.datas,id: \.self) { array in HStack{ ForEach(array.utf8CString, id: \.self) { element in Text("\(element)") } } } } }
问题分析与解决方案
1. Model解码失败的核心问题
PublisherPlan枚举仅声明了Decodable,但未实现自定义解码逻辑,JSONDecoder无法自动判断数组元素是Int还是String类型,导致解析报错。需为枚举添加自定义初始化方法:
public enum PublisherPlan: Decodable { case integer(Int) case string(String) public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let intValue = try? container.decode(Int.self) { self = .integer(intValue) return } if let stringValue = try? container.decode(String.self) { self = .string(stringValue) return } throw DecodingError.typeMismatch(PublisherPlan.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported type for PublisherPlan")) } }
2. ViewModel数据类型不匹配且未赋值
ViewModel中@Published var datas = [String]()是一维字符串数组,与解析出的[[PublisherPlan]]二维枚举数组类型不匹配,且未将解析结果赋值给存储变量。修改ViewModel如下:
class ReadData: ObservableObject { @Published var plans: [[PublisherPlan]] = [] func getData() async { guard let url = URL(string: "********api/plans") else { return } do { let (data, _) = try await URLSession.shared.data(from: url) await MainActor.run { let model = try JSONDecoder().decode(SchoolPlanModel.self, from: data) self.plans = model.publisherPlans } } catch { print("---> error: \(error)") } } // 转换为二维字符串数组,方便View直接展示 var planStrings: [[String]] { plans.map { row in row.map { item in switch item { case .integer(let num): return "\(num)" case .string(let str): return str } } } } }
3. View展示逻辑错误
原View遍历一维字符串数组后,再遍历字符串的utf8CString会把每个字符拆成ASCII值显示,完全不符合需求。修改View适配二维数组结构:
struct SchoolPlanView: View{ @StateObject var list = ReadData() var body: some View{ List { ForEach(list.planStrings, id: \.self) { row in HStack { ForEach(row, id: \.self) { item in Text(item) .frame(maxWidth: .infinity, alignment: .leading) .padding() } } } } .onAppear { Task { await list.getData() } } } }
4. 额外注意事项
- 确保API返回的JSON格式与示例一致,无额外嵌套或类型错误;
- 补充网络错误处理逻辑,比如无网络、API返回错误码等场景;
- 若需要更美观的表格布局,可使用
LazyVGrid或第三方表格库优化。
内容的提问来源于stack exchange,提问作者user20121009
相关产品推荐
相关产品推荐

