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

SwiftUI中内部API结构体解析失败问题求助

问题描述

我是SwiftUI新手,此前调用外部API的代码运行正常。添加结构体Partei后,出现三类错误:

  • 58:28 Generic parameter 'T' could not be inferred -> 调用Foundation.JSONDecoder的decode(_:from:)函数时触发
  • 58:43 Cannot convert value of type 'module' to expected argument type 'T.Type'
  • 108:88 Missing argument for parameter 'partei' in call -> 此处声明了init(politician:partei:)

需求:在PoliticianDetailView中展示Partei结构体的文本信息,计划添加代码:

List(viewModel.parteien, id: \.parteiID) { partei in
    Text("Partei: \(partei.namePartei)")
}
错误修复与完整代码

错误1、2的修复

ParteiDataService中调用decode(Parteien.self, from: data)时,Parteien结构体未定义,导致JSON解析失败。需要添加和Politicians结构一致的解析结构体:

struct Parteien: Decodable {
    let data: [Partei]
}

错误3的修复

PoliticianDetailView声明了let partei: Partei参数,但Politician结构体已经包含partei属性,无需单独传递该参数。直接移除PoliticianDetailView中的let partei: Partei即可,跳转时只传politician参数。

完整修复代码

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]
}

// 新增政党列表解析结构体
struct Parteien: Decodable {
    let data: [Partei]
}

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() {
        // 可在此添加额外初始化逻辑
    }
    
    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
    @StateObject private var viewModel = PoliticianDetailViewModel()

    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("Partei: \(politician.partei.namePartei)")
            
            // 展示所有政党列表(按需求保留)
            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()
    }
}

额外说明

  • 如果仅需展示当前政客的政党信息,无需调用政党列表API,直接使用politician.partei.namePartei即可,可简化代码移除ParteiDataService和PoliticianDetailViewModel相关逻辑。
  • 如果需要展示全量政党列表,保留当前API调用逻辑即可,修复后的代码可正常运行。

内容的提问来源于stack exchange,提问作者Alko Nama

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:07:04