如何从SwiftUI API请求返回的列表数据中提取单个String/Int值
问题原因
崩溃的核心原因是视图渲染时直接访问了空数组的指定下标:你声明@State var rockets: [Rockets] = []初始值是空数组,在网络请求完成返回数据之前,SwiftUI渲染Text(rockets[1].name)时会触发数组越界错误,直接导致程序崩溃。
另外你的Rockets数据模型存在解析隐患:你自行给id赋值了UUID,但SpaceX的API返回的id是字符串类型的唯一标识,当前你只解析name字段暂时不会报错,但后续扩展解析字段时会出现解析失败的问题。
修复方案
1. 安全访问数组元素
不要直接硬编码下标访问数组,先判断数组长度是否符合要求,或者用安全取值的方式避免越界:
struct RocketStatistics: View { @State var rockets: [Rockets] = [] var body: some View { VStack{ // 先判断数组长度是否大于1,再访问下标1的元素,否则显示占位文本 if rockets.count > 1 { Text(rockets[1].name) } else { Text("加载中...") } } .onAppear { apiCall().getRockets { (rockets) in self.rockets = rockets } } } }
2. 修正数据模型
添加CodingKeys排除你自定义的本地id字段,避免后续解析其他字段时报错;如果需要使用API返回的官方ID,直接对应字段即可:
struct Rockets: Codable, Identifiable { // 本地用的UUID let id = UUID() let name : String // 后续要取Int类型字段直接定义即可,比如火箭的成本字段 let cost_per_launch: Int // 只解析JSON中存在的字段,排除本地自定义的id enum CodingKeys: String, CodingKey { case name, cost_per_launch } }
3. 全局使用API数据
如果需要在整个项目中共享请求到的火箭数据,使用SwiftUI的ObservableObject做全局状态管理即可:
首先创建全局状态类:
class RocketViewModel: ObservableObject { @Published var rockets: [Rockets] = [] func getRockets() { guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else { return } URLSession.shared.dataTask(with: url) { (data, _, _) in guard let data = data else { return } do { let rockets = try JSONDecoder().decode([Rockets].self, from: data) DispatchQueue.main.async { self.rockets = rockets } } catch { print("解析错误: \(error)") } } .resume() } }
然后在App入口处注入全局状态:
@main struct YourApp: App { @StateObject var rocketVM = RocketViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(rocketVM) } } }
之后任意视图都可以直接调用数据,无需重复发起请求:
struct AnyView: View { // 注入全局状态 @EnvironmentObject var rocketVM: RocketViewModel var body: some View { VStack { // 安全访问任意字段,包括Int类型 if rocketVM.rockets.count > 0 { Text("火箭名称:\(rocketVM.rockets[0].name)") Text("发射成本:\(rocketVM.rockets[0].cost_per_launch) 美元") } } .onAppear { // 只需要在App启动时调用一次即可,其他页面不用重复请求 if rocketVM.rockets.isEmpty { rocketVM.getRockets() } } } }
Int类型字段使用建议
你只需要在Rockets模型中按照API返回的字段名定义对应Int类型的属性即可,JSONDecoder会自动完成解析,比如API返回的height.meters、diameter.meters、cost_per_launch都是数值类型,需要层级解析的话按照JSON结构定义嵌套结构体即可。
内容的提问来源于stack exchange,提问作者jesse voorn
相关产品推荐
相关产品推荐

