如何在视图中正确使用 extern 结构?

How to use extern Structure in a View correctly?

提问人:Sergei Kirasirov 提问时间:6/21/2023 更新时间:6/21/2023 访问量:28

问:

我找不到在 ContentView 中使用我的结构的方法。 结构正确填充,但仅此而已。 如何替换视图中的示例数组:

let countries = ["Germany", "Egypt", "Italy"] // example

来自以下国家/地区的真实阵列:

let countries: CountriesWikiData = await decodeWikiData(from: countriesQuery)
struct CountriesWikiData: Decodable {
    let head: CountriesHead
    let results: CountriesResults
}

struct CountriesHead: Decodable {
    let vars: [String]
}

struct CountriesResults: Decodable {
    let bindings: [CountriesBindings]
}

struct CountriesBindings: Decodable {
    let country: [String: String]
    let countryLabel: [String: String]
}


func decodeWikiData<T: Decodable>(from url: String) async -> T {
    
    let url = URL(string: url)!
    let request = URLRequest(url: url)
    
    do {
        let (data, _) = try await URLSession.shared.data(for: request)
        guard
            let jsonObj = try? JSONSerialization.jsonObject(with: data, options: []),
            let jsonData = try? JSONSerialization.data(withJSONObject: jsonObj, options: .prettyPrinted)
        else {
            fatalError("Cannot convert data to JSON.")
        }
        guard let strc = try? JSONDecoder().decode(T.self, from: jsonData)
        else {
            fatalError("Cannot make a structure.")
        }
        return strc // Wheeeee!!
    } catch {
        fatalError("Cannot get data from URL.")
    }
}


struct ContentView: View {
        
    /*
    SELECT DISTINCT ?country ?countryLabel
    WHERE
    {
      ?country wdt:P31 wd:Q3624078.
      SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
    }
    */
    
    let countriesQuery = "https://query.wikidata.org/sparql?query=SELECT%20DISTINCT%20%3Fcountry%20%3FcountryLabel%0AWHERE%0A%7B%0A%20%20%3Fcountry%20wdt%3AP31%20wd%3AQ3624078.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22en%22%20%7D%0A%7D&format=json"
    
    let countries = ["Germany", "Egypt", "Italy"] // example
    @State var selectedCountry = ""
    
    //let countries: CountriesWikiData = await decodeWikiData(from: countriesQuery)

    var body: some View {
        VStack {
            Picker("", selection: $selectedCountry) {
                ForEach(countries, id: \.self) {
                    Text($0)
                }
            }
        }
    }
}

结构 swift3

评论


答:

1赞 workingdog support Ukraine 6/21/2023 #1

您可以尝试此方法,以使用模型结构显示 API 调用的结果,如示例代码中所示。

该方法使用修改后的 with 和 结构。 视图中的更新修饰符 和 a 与重要的CountriesBindingsCountryCountryLabelfunc decodeWikiData(...).task {...}Picker.tag(country.value)

struct CountriesWikiData: Decodable {
    let head: CountriesHead
    let results: CountriesResults
}

struct CountriesHead: Decodable {
    let vars: [String]
}

struct CountriesResults: Decodable {
    let bindings: [CountriesBindings]
}

struct CountriesBindings: Codable {
    let country: Country
    let countryLabel: CountryLabel
}

struct Country: Identifiable, Codable, Hashable {
    let id = UUID()
    let type: String
    let value: String
}

struct CountryLabel: Identifiable, Codable, Hashable {
    let id = UUID()
    let xmlLang: String
    let type: String
    let value: String

    enum CodingKeys: String, CodingKey {
        case xmlLang = "xml:lang"
        case type, value
    }
}

struct ContentView: View {
    let countriesQuery = "https://query.wikidata.org/sparql?query=SELECT%20DISTINCT%20%3Fcountry%20%3FcountryLabel%0AWHERE%0A%7B%0A%20%20%3Fcountry%20wdt%3AP31%20wd%3AQ3624078.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22en%22%20%7D%0A%7D&format=json"
    
    @State var countries: [CountryLabel] = []
    @State var selectedCountry: String = ""

    var body: some View {
        VStack {
            Text("selected country: \(selectedCountry)")
            Picker("", selection: $selectedCountry) {
                ForEach(countries) { country in
                    Text(country.value).tag(country.value)
                }
            }
        }
        .task {
            let response: CountriesWikiData? = await decodeWikiData(from: countriesQuery)
            if let resp = response {
                for bin in resp.results.bindings {
                    countries.append(bin.countryLabel)
                }
            }
            if let first = countries.first {
                selectedCountry = first.value
            }
        }
    }
    
    func decodeWikiData<T: Decodable>(from url: String) async -> T? {
        if let url = URL(string: url) {
            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                return try JSONDecoder().decode(T.self, from: data)
            } catch {
                print(error)
            }
        }
        return nil
    }
    
}

评论

0赞 Sergei Kirasirov 6/21/2023
哇。。。多谢!太棒了。