为什么我的Swift UI项目没有通过JSON接收数据?

7hiiyaii  于 2023-04-28  发布在  Swift
关注(0)|答案(1)|浏览(98)

我想用Json学习Swift UI。我想使用一个公共API来学习它。不幸的是,它总是显示错误:无法读取数据,因为格式不正确。
API文档https://www.abgeordnetenwatch.de/api/entitaeten/politician API数据https://www.abgeordnetenwatch.de/api/v2/politicians
代码如下

import SwiftUI

struct ContentView: View {
    @State private var politicians: [Politician] = []
    @State private var selectedPolitician: Politician? = nil
    
    var body: some View {
        NavigationView {
            List(politicians, id: \.id) { politician in
                Button(action: {
                    self.selectedPolitician = politician
                }) {
                    Text("\(politician.firstName) \(politician.lastName)")
                }
            }
            .navigationTitle("Politicians")
            .onAppear {
                self.loadPoliticians()
            }
            
            if let politician = selectedPolitician {
                PoliticianDetailView(politician: politician)
            }
        }
    }
    
    private func loadPoliticians() {
        let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    let decoder = JSONDecoder()
                    let politicians = try decoder.decode([Politician].self, from: data)
                    DispatchQueue.main.async {
                        self.politicians = politicians
                    }
                } catch {
                    print(error.localizedDescription)
                }
            }
        }.resume()
    }
}

struct PoliticianDetailView: View {
    let politician: Politician
    
    var body: some View {
        VStack {
            Text("\(politician.firstName) \(politician.lastName)")
                .font(.title)
            Text("ID: \(politician.id)")
            Text("Sex: \(politician.sex)")
        }
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct Politician: Codable, Identifiable {
    let id: Int
    let firstName: String
    let lastName: String
    let sex: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case lastName = "last_name"
        case sex
    }
}
6fe3ivhb

6fe3ivhb1#

你需要有一组结构体来镜像你从服务器获得的JSON数据。所以你得到的错误是因为你没有把JSON数据解码成正确的结构模型。
将json数据复制并粘贴到https://app.quicktype.io/后,您需要做更多的工作来调整生成的代码以满足您的目的。
以下代码解码JSON数据并将其放入[Politician]数组中。该代码还包含较新的NavigationStack,而不是已弃用的NaviagtionView。对我来说很好。

struct ContentView: View {
    @State private var politicians: [Politician] = []
    @State private var path = NavigationPath()  // <--- here
    
    var body: some View {
        NavigationStack(path: $path) {  // <--- here
            if politicians.isEmpty { ProgressView() }
            List(politicians, id: \.id) { politician in
                Button(action: {
                    path.append(politician) // <--- here
                }) {
                    Text("\(politician.firstName) \(politician.lastName)")
                }
            }
            .navigationTitle("Politicians")
            .navigationDestination(for: Politician.self) { poli in  // <--- here
                PoliticianDetailView(politician: poli)
            }
        }
        .onAppear {
            loadPoliticians()
        }
    }
    
    private func loadPoliticians() {
        let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    // --- here
                    let results = try JSONDecoder().decode(PoliResponse.self, from: data)
                    politicians = results.data  // <--- here
                } catch {
                    print(error.localizedDescription)
                }
            }
        }.resume()
    }
}

struct PoliticianDetailView: View {
    let politician: Politician
    
    var body: some View {
        VStack {
            Text("\(politician.firstName) \(politician.lastName)").font(.title)
            Text("ID: \(politician.id)")
            Text("Sex: \(politician.sex ?? "")")  // <--- here
        }
        .navigationBarTitleDisplayMode(.inline)
    }
}

// the response you get from the server
struct PoliResponse: Codable {
    let meta: Meta
    let data: [Politician]
}

struct Politician: Identifiable, Codable, Hashable {
    let id: Int
    let entityType, label, apiURL, abgeordnetenwatchURL, firstName, lastName: String
    let party: Party
    let sex, birthName, partyPast, education, residence, occupation: String?
    let yearOfBirth, statisticQuestions, statisticQuestionsAnswered: Int?
    let extIDBundestagsverwaltung, qidWikidata, fieldTitle: String?
    
    enum CodingKeys: String, CodingKey {
        case id, label, sex, party, education, residence, occupation
        case entityType = "entity_type"
        case apiURL = "api_url"
        case abgeordnetenwatchURL = "abgeordnetenwatch_url"
        case firstName = "first_name"
        case lastName = "last_name"
        case birthName = "birth_name"
        case yearOfBirth = "year_of_birth"
        case partyPast = "party_past"
        case statisticQuestions = "statistic_questions"
        case statisticQuestionsAnswered = "statistic_questions_answered"
        case extIDBundestagsverwaltung = "ext_id_bundestagsverwaltung"
        case qidWikidata = "qid_wikidata"
        case fieldTitle = "field_title"
    }
}

struct Party: Identifiable, Codable, Hashable {
    let id: Int
    let entityType, label, apiURL: String
    
    enum CodingKeys: String, CodingKey {
        case id, label
        case entityType = "entity_type"
        case apiURL = "api_url"
    }
}

struct Meta: Codable {
    let abgeordnetenwatchAPI: AbgeordnetenwatchAPI
    let status, statusMessage: String
    let result: Result
    
    enum CodingKeys: String, CodingKey {
        case status, result
        case abgeordnetenwatchAPI = "abgeordnetenwatch_api"
        case statusMessage = "status_message"
    }
}

struct AbgeordnetenwatchAPI: Codable {
    let version, changelog, licence, documentation: String
    let licenceLink: String
    
    enum CodingKeys: String, CodingKey {
        case version, changelog, licence, documentation
        case licenceLink = "licence_link"
    }
}

struct Result: Codable {
    let count, total, rangeStart, rangeEnd: Int
    
    enum CodingKeys: String, CodingKey {
        case count, total
        case rangeStart = "range_start"
        case rangeEnd = "range_end"
    }
}

相关问题