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

如何从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 22:27:02