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

如何在SwiftUI的ContentView中正确使用外部结构体替换示例数组?

解决ContentView中替换示例数组为自定义结构体数据的问题

步骤1:调整ContentView的状态变量

把原示例数组替换为存储国家名称的状态变量,保留查询链接:

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 private var countryLabels: [String] = [] // 存储提取后的国家名称数组
    @State var selectedCountry = ""
    
    // ...
}

步骤2:添加异步数据加载逻辑

用.task修饰符在视图初始化时异步加载数据,从自定义结构体中提取国家名称并更新状态变量:

var body: some View {
    VStack {
        Picker("", selection: $selectedCountry) {
            ForEach(countryLabels, id: \.self) {
                Text($0)
            }
        }
    }
    .task {
        // 异步获取并解码结构体数据
        let wikiData: CountriesWikiData = await decodeWikiData(from: countriesQuery)
        // 从结构体中提取国家名称(取countryLabel的value字段)
        countryLabels = wikiData.results.bindings.compactMap { $0.countryLabel["value"] }
        // 可选:设置默认选中第一个国家
        if let firstCountry = countryLabels.first {
            selectedCountry = firstCountry
        }
    }
}

步骤3:优化解码函数(可选)

移除原函数中多余的JSON序列化步骤,直接用JSONDecoder解码,同时优化错误提示:

func decodeWikiData<T: Decodable>(from urlString: String) async -> T {
    guard let url = URL(string: urlString) else {
        fatalError("无效的查询URL")
    }
    let request = URLRequest(url: url)
    
    do {
        let (data, _) = try await URLSession.shared.data(for: request)
        return try JSONDecoder().decode(T.self, from: data)
    } catch {
        fatalError("数据加载或解码失败:\(error.localizedDescription)")
    }
}

完整修改后的ContentView代码

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 private var countryLabels: [String] = []
    @State var selectedCountry = ""
    
    var body: some View {
        VStack {
            Picker("", selection: $selectedCountry) {
                ForEach(countryLabels, id: \.self) {
                    Text($0)
                }
            }
        }
        .task {
            let wikiData: CountriesWikiData = await decodeWikiData(from: countriesQuery)
            countryLabels = wikiData.results.bindings.compactMap { $0.countryLabel["value"] }
            if let first = countryLabels.first {
                selectedCountry = first
            }
        }
    }
}

关键说明

  • 视图属性中不能直接使用await,必须在.task这类异步上下文里调用异步函数
  • Picker需要字符串数组,因此要从CountriesWikiData结构体中提取countryLabel的具体值
  • 用@State修饰countryLabels,确保数据更新时视图自动刷新

内容的提问来源于stack exchange,提问作者Sergei Kirasirov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:32:02