如何在 SwiftUI 中从 API 获取值(不是数组)
How to get value (not array) from API in SwiftUI
我使用这个 API:https://api.spacexdata.com/v4/rockets。
通过一些探索,我决定使用这个 API getter:
let rocketsData: [Rocket] = [] //<-- Spoiler: This is my problem
class Api {
func getRockets(completion: @escaping ([Rocket]) -> ()) {
guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
let rockets = try JSONDecoder().decode([Rocket].self, from: data!)
DispatchQueue.main.async {
completion(rockets)
}
} catch {
print(error.localizedDescription)
}
}
.resume()
}
}
这是我的模特:
struct StageInfo: Codable {
let engines: Int
let fuelAmountTons: Double
let burnTimeSec: Int?
enum CodingKeys: String, CodingKey {
case engines
case fuelAmountTons = "fuel_amount_tons"
case burnTimeSec = "burn_time_sec"
}
}
struct Rocket: Codable, Identifiable {
let id = UUID()
let name: String
let firstFlight: String
let country: String
let costPerLaunch: Int
let firstStage: StageInfo
let secondStage: StageInfo
enum CodingKeys: String, CodingKey {
case id
case name
case firstFlight = "first_flight"
case country
case costPerLaunch = "cost_per_launch"
case firstStage = "first_stage"
case secondStage = "second_stage"
}
}
通过这个模型,我可以获得一个值数组,并且我可以在我的视图中仅使用列表或 ForEach 循环来使用这个数组。但是,如果我不想使用数组,而是使用一些值怎么办?
例如,在这个视图中我使用 ForEach 循环,这就是为什么一切都完美的原因:
struct ContentView: View {
@State var rockets = [Rocket]() // <-- Here is the array I was talking about
var body: some View {
NavigationView {
List {
ForEach(rockets) { item in // <-- ForEach loop to get values from the array
NavigationLink(destination: RocketDetailView(rocket: item)) {
RocketRowView(rocket: item)
.padding(.vertical, 4)
}
}
} //: LIST
.navigationTitle("Rockets")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
isShowingSettings = true
}) {
Image(systemName: "slider.horizontal.3")
} //: BUTTON
.sheet(isPresented: $isShowingSettings) {
SettingsView()
}
}
} //: TOOLBAR
} //: NAVIGATION
.onAppear() {
Api().getRockets { rockets in // <-- Method to get the array from the API
self.rockets = rockets
}
}
}
}
//MARK: - PREVIEW
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
最后,我有一个视图,其中包含一个项目,我用它在 另一个视图 中创建 这个项目的列表:
struct RocketRowView: View {
var rocket: Rocket // <-- Here I don't need to use an array as previously, so I create a usual variable with Rocket instance
var body: some View {
HStack {
Image("Falcon-9")
.resizable()
.scaledToFill()
.frame(width: 80, height: 80, alignment: .center)
.background(Color.teal)
.cornerRadius(8)
VStack(alignment: .leading, spacing: 5) {
Text(rocket.name) // <-- So this surely doesn't work
.font(.title2)
.fontWeight(.bold)
Text(rocket.country) // <-- And this doesn't work neither
.font(.caption)
.foregroundColor(Color.secondary)
}
} //: HSTACK <-- And I can't use here inside onAppear the method that I used before to get the array, because I don't need the array
}
}
struct RocketRowView_Previews: PreviewProvider {
static var previews: some View {
RocketRowView(rocket: rocketsData[0]) // <-- Because of the variable at the top of this View I have to add missing argument for parameter 'rocket' in call. Immediately after that I get an error: "App crashed due to an out of range index"
}
}
正如我在上面的评论中所写,我在我的 RocketRowView() 中创建了一个类型为 Rocket 的变量 rocket,因此我必须为参数 [=36 添加缺少的参数=] 在 RocketRowView_Previews 中。在这里我收到一个错误:“应用程序因索引超出范围而崩溃”。我不明白为什么 let rocketsData: [Rocket] = [] 是空的,以及我如何在没有 ForEach 循环的情况下在我的视图中使用火箭变量。
请各位高手帮帮我。我已经绞尽脑汁想了整整一个星期,我应该做什么。
预览区的数据与接收到的数据无关。如果您想要预览,则必须提供自定义实例。
通常的方法是像这样向结构添加静态 example
属性
struct StageInfo: Codable {
let engines: Int
let fuelAmountTons: Double
let burnTimeSec: Int?
enum CodingKeys: String, CodingKey {
case engines
case fuelAmountTons = "fuel_amount_tons"
case burnTimeSec = "burn_time_sec"
}
static let firstStage = StageInfo(engines: 1, fuelAmountTons: 44.3, burnTimeSec: 169)
static let secondStage = StageInfo(engines: 1, fuelAmountTons: 3.30, burnTimeSec: 378)
}
struct Rocket: Codable, Identifiable {
let id = UUID()
let name: String
let firstFlight: String
let country: String
let costPerLaunch: Int
let firstStage: StageInfo
let secondStage: StageInfo
enum CodingKeys: String, CodingKey {
case id
case name
case firstFlight = "first_flight"
case country
case costPerLaunch = "cost_per_launch"
case firstStage = "first_stage"
case secondStage = "second_stage"
}
static let example = Rocket(id: UUID(), name: "Falcon 1", firstFlight: "2006-03-24", country: "Republic of the Marshall Islands", costPerLaunch: 6700000, firstStage: StageInfo.firstStage, secondStage: StageInfo.secondStage)
}
那就参考例子吧
struct RocketRowView_Previews: PreviewProvider {
static var previews: some View {
RocketRowView(rocket: Rocket.example)
}
}
我使用这个 API:https://api.spacexdata.com/v4/rockets。 通过一些探索,我决定使用这个 API getter:
let rocketsData: [Rocket] = [] //<-- Spoiler: This is my problem
class Api {
func getRockets(completion: @escaping ([Rocket]) -> ()) {
guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
let rockets = try JSONDecoder().decode([Rocket].self, from: data!)
DispatchQueue.main.async {
completion(rockets)
}
} catch {
print(error.localizedDescription)
}
}
.resume()
}
}
这是我的模特:
struct StageInfo: Codable {
let engines: Int
let fuelAmountTons: Double
let burnTimeSec: Int?
enum CodingKeys: String, CodingKey {
case engines
case fuelAmountTons = "fuel_amount_tons"
case burnTimeSec = "burn_time_sec"
}
}
struct Rocket: Codable, Identifiable {
let id = UUID()
let name: String
let firstFlight: String
let country: String
let costPerLaunch: Int
let firstStage: StageInfo
let secondStage: StageInfo
enum CodingKeys: String, CodingKey {
case id
case name
case firstFlight = "first_flight"
case country
case costPerLaunch = "cost_per_launch"
case firstStage = "first_stage"
case secondStage = "second_stage"
}
}
通过这个模型,我可以获得一个值数组,并且我可以在我的视图中仅使用列表或 ForEach 循环来使用这个数组。但是,如果我不想使用数组,而是使用一些值怎么办? 例如,在这个视图中我使用 ForEach 循环,这就是为什么一切都完美的原因:
struct ContentView: View {
@State var rockets = [Rocket]() // <-- Here is the array I was talking about
var body: some View {
NavigationView {
List {
ForEach(rockets) { item in // <-- ForEach loop to get values from the array
NavigationLink(destination: RocketDetailView(rocket: item)) {
RocketRowView(rocket: item)
.padding(.vertical, 4)
}
}
} //: LIST
.navigationTitle("Rockets")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
isShowingSettings = true
}) {
Image(systemName: "slider.horizontal.3")
} //: BUTTON
.sheet(isPresented: $isShowingSettings) {
SettingsView()
}
}
} //: TOOLBAR
} //: NAVIGATION
.onAppear() {
Api().getRockets { rockets in // <-- Method to get the array from the API
self.rockets = rockets
}
}
}
}
//MARK: - PREVIEW
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
最后,我有一个视图,其中包含一个项目,我用它在 另一个视图 中创建 这个项目的列表:
struct RocketRowView: View {
var rocket: Rocket // <-- Here I don't need to use an array as previously, so I create a usual variable with Rocket instance
var body: some View {
HStack {
Image("Falcon-9")
.resizable()
.scaledToFill()
.frame(width: 80, height: 80, alignment: .center)
.background(Color.teal)
.cornerRadius(8)
VStack(alignment: .leading, spacing: 5) {
Text(rocket.name) // <-- So this surely doesn't work
.font(.title2)
.fontWeight(.bold)
Text(rocket.country) // <-- And this doesn't work neither
.font(.caption)
.foregroundColor(Color.secondary)
}
} //: HSTACK <-- And I can't use here inside onAppear the method that I used before to get the array, because I don't need the array
}
}
struct RocketRowView_Previews: PreviewProvider {
static var previews: some View {
RocketRowView(rocket: rocketsData[0]) // <-- Because of the variable at the top of this View I have to add missing argument for parameter 'rocket' in call. Immediately after that I get an error: "App crashed due to an out of range index"
}
}
正如我在上面的评论中所写,我在我的 RocketRowView() 中创建了一个类型为 Rocket 的变量 rocket,因此我必须为参数 [=36 添加缺少的参数=] 在 RocketRowView_Previews 中。在这里我收到一个错误:“应用程序因索引超出范围而崩溃”。我不明白为什么 let rocketsData: [Rocket] = [] 是空的,以及我如何在没有 ForEach 循环的情况下在我的视图中使用火箭变量。 请各位高手帮帮我。我已经绞尽脑汁想了整整一个星期,我应该做什么。
预览区的数据与接收到的数据无关。如果您想要预览,则必须提供自定义实例。
通常的方法是像这样向结构添加静态 example
属性
struct StageInfo: Codable {
let engines: Int
let fuelAmountTons: Double
let burnTimeSec: Int?
enum CodingKeys: String, CodingKey {
case engines
case fuelAmountTons = "fuel_amount_tons"
case burnTimeSec = "burn_time_sec"
}
static let firstStage = StageInfo(engines: 1, fuelAmountTons: 44.3, burnTimeSec: 169)
static let secondStage = StageInfo(engines: 1, fuelAmountTons: 3.30, burnTimeSec: 378)
}
struct Rocket: Codable, Identifiable {
let id = UUID()
let name: String
let firstFlight: String
let country: String
let costPerLaunch: Int
let firstStage: StageInfo
let secondStage: StageInfo
enum CodingKeys: String, CodingKey {
case id
case name
case firstFlight = "first_flight"
case country
case costPerLaunch = "cost_per_launch"
case firstStage = "first_stage"
case secondStage = "second_stage"
}
static let example = Rocket(id: UUID(), name: "Falcon 1", firstFlight: "2006-03-24", country: "Republic of the Marshall Islands", costPerLaunch: 6700000, firstStage: StageInfo.firstStage, secondStage: StageInfo.secondStage)
}
那就参考例子吧
struct RocketRowView_Previews: PreviewProvider {
static var previews: some View {
RocketRowView(rocket: Rocket.example)
}
}