提问人:Alko Nama 提问时间:8/26/2023 更新时间:8/26/2023 访问量:57
为什么我的内部 API 结构无法在 SwiftUI 中成功执行?
Why isn't my internal API structure executing successfully in SwiftUI?
问:
我是一个菜鸟,我对自己的问题感到困惑。我的理解可能有限,我查找了一些类似的问题,我发现了不同的问题,比如我缺少 init(我不知道 init 是如何工作的)。
所以!我尝试通过JSON使用外部API。你可以在这里找到它(https://www.abgeordnetenwatch.de/api/v2/politicians)。我的代码工作正常,直到我想在我工作的“政治家”结构旁边添加一个内部结构“party”(在我的代码“partei”中)。经过研究,我认为我需要像我的代码一样构建代码结构。我为 id 和 label 使用了不同的名称,以使其通过编码键工作。
我目前遇到这些错误
“58:28 无法推断通用参数'T' ->在调用函数 'decode(_:from:)' (Foundation.JSONDecoder) 时
58:43 无法将类型“模块”的值转换为预期的参数类型“T.Type”
108:88 调用中缺少参数“partei”的参数 ->'init(politician:partei:)'在这里宣布
我的计划是将内部结构 Partei 中的另一个文本添加到我的 PoliticanDetailView 中
List(viewModel.parteien, id: \.parteiID) { partei in
Text("Partei: \(partei.namePartei)")
我的整个混乱代码,如果它可能不符合礼仪,我很抱歉
import SwiftUI
struct Partei: Decodable {
let parteiID: Int
let namePartei: String
private enum CodingKeys: String, CodingKey {
case parteiID = "id"
case namePartei = "label"
}
}
struct Politician: Decodable {
let id: Int
let firstName: String
let lastName: String
let birthYear: Int?
let beruf: String?
let geschlecht: String?
let partei: Partei
enum CodingKeys: String, CodingKey {
case id
case firstName = "first_name"
case lastName = "last_name"
case birthYear = "year_of_birth"
case beruf = "occupation"
case geschlecht = "sex"
case partei
}
}
struct Politicians: Decodable {
let data: [Politician]
}
actor PoliticianDataService {
func fetchPoliticians() async throws -> [Politician] {
guard let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians") else { return [] }
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
let politicians = try decoder.decode(Politicians.self, from: data)
return politicians.data
}
}
actor ParteiDataService {
func fetchParteien() async throws -> [Partei] {
guard let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/parties") else { return [] }
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
let parteien = try decoder.decode(Parteien.self, from: data)
return parteien.data
}
}
@MainActor
class PoliticianListViewModel: ObservableObject {
@Published var politicians = [Politician]()
private var dataService = PoliticianDataService()
init() {
// Additional setup could be done here if needed
}
func fetchPoliticians() {
Task {
do {
let fetchedPoliticians = try await dataService.fetchPoliticians()
politicians = fetchedPoliticians
} catch {
print(error)
}
}
}
}
class PoliticianDetailViewModel: ObservableObject {
@Published var parteien = [Partei]()
private var dataService = ParteiDataService()
func fetchParteien() {
Task {
do {
let fetchedParteien = try await dataService.fetchParteien()
parteien = fetchedParteien
} catch {
print(error)
}
}
}
}
struct ContentView: View {
@StateObject var viewModel = PoliticianListViewModel()
var body: some View {
NavigationView {
List(viewModel.politicians, id: \.id) { politician in
NavigationLink(destination: PoliticianDetailView(politician: politician)) {
Text("\(politician.firstName) \(politician.lastName)")
}
}
.navigationTitle("Politiker in Deutschland")
}
.onAppear {
viewModel.fetchPoliticians()
}
}
}
struct PoliticianDetailView: View {
let politician: Politician
let partei: Partei
@StateObject private var viewModel = PoliticianDetailViewModel() // Create a ViewModel
var body: some View {
VStack {
Text("Name: \(politician.firstName) \(politician.lastName)")
Text("Geburtsjahr \(politician.birthYear ?? 0)")
Text("Alter: \(calculateAge(birthYear: politician.birthYear ?? 0)) Jahre")
Text("Beschäftigt als \(politician.beruf ?? "Unbekannt")")
Text("Geschlecht: \(politician.geschlecht ?? "Unbekannt")")
List(viewModel.parteien, id: \.parteiID) { partei in
Text("Partei: \(partei.namePartei)")
}
.onAppear {
viewModel.fetchParteien()
}
}
.navigationTitle("\(politician.firstName) \(politician.lastName)")
}
func calculateAge(birthYear: Int) -> Int {
let currentYear = Calendar.current.component(.year, from: Date())
return currentYear - birthYear
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
答:
您的代码中有一些错误。
第一个错误的发生是因为您想要解码,但没有结构。Parteien.self
Parteien
struct Parteien: Decodable {
let data: [Partei]
}
但是,你并不真正需要这个结构或获取参与方,因为参与方已经在结构中。Politician
发生第二个错误的原因是,您已在结构中声明了一个属性,但未传入值。同样,您不需要该属性,因为它已经在 .partei
PoliticianDetailView
Politician
与其使用 a,不如使用额外的 .List
Text
struct PoliticianDetailView: View {
let politician: Politician
var body: some View {
VStack {
Text("Name: \(politician.firstName) \(politician.lastName)")
Text("Geburtsjahr \(politician.birthYear ?? 0)")
Text("Alter: \(calculateAge(birthYear: politician.birthYear ?? 0)) Jahre")
Text("Beschäftigt als \(politician.beruf ?? "Unbekannt")")
Text("Geschlecht: \(politician.geschlecht ?? "Unbekannt")")
Text("Party: \(politician.partei.namePartei)")
}
.navigationTitle("\(politician.firstName) \(politician.lastName)")
}
func calculateAge(birthYear: Int) -> Int {
let currentYear = Calendar.current.component(.year, from: Date())
return currentYear - birthYear
}
}
最后,你会得到一个运行时错误,因为你的 for 没有将属性从 JSON 映射到你的结构中:CodingKeys
Partei
party
partei
struct Politician: Decodable {
let id: Int
let firstName: String
let lastName: String
let birthYear: Int?
let beruf: String?
let geschlecht: String?
let partei: Partei
enum CodingKeys: String, CodingKey {
case id
case firstName = "first_name"
case lastName = "last_name"
case birthYear = "year_of_birth"
case beruf = "occupation"
case geschlecht = "sex"
case partei = "party"
}
}
此外,您可以调整您的年龄计算,使其不会将没有出生年份的人显示为 2023 年
struct PoliticianDetailView: View {
let politician: Politician
var body: some View {
VStack {
Text("Name: \(politician.firstName) \(politician.lastName)")
Text("Geburtsjahr \(politician.birthYear ?? 0)")
Text("Alter: \(calculateAge(birthYear: politician.birthYear))")
Text("Beschäftigt als \(politician.beruf ?? "Unbekannt")")
Text("Geschlecht: \(politician.geschlecht ?? "Unbekannt")")
Text("Party: \(politician.partei.namePartei)")
}
.navigationTitle("\(politician.firstName) \(politician.lastName)")
}
func calculateAge(birthYear: Int?) -> String {
guard let birthYear else {
return "---"
}
let currentYear = Calendar.current.component(.year, from: Date())
return String(currentYear - birthYear)+" jahre"
}
}
并添加按姓氏排序:
actor PoliticianDataService {
func fetchPoliticians() async throws -> [Politician] {
guard let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians") else { return [] }
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
let politicians = try decoder.decode(Politicians.self, from: data)
return politicians.data.sorted {
$0.lastName < $1.lastName
}
}
}
评论
let partei: Partei
PoliticianView
评论