SwiftUI国旗问答App:国旗存储方案选择与JSON解析问题求助
SwiftUI国旗问答App:国旗存储方案选择与JSON解析问题求助
嘿,作为SwiftUI新手能自己动手做国旗问答App已经很棒啦!别担心,这些问题都是新手入门时很常见的,我来帮你一步步梳理~
一、国旗存储方案怎么选?
针对你的需求,三种方案各有优劣,适合不同阶段:
- 本地Asset文件夹存储:最适合新手练手,操作简单,不需要网络就能加载国旗,速度快。缺点是如果以后要新增/修改国旗,必须更新App版本。如果你的题库一开始就固定,这个方案最省心。
- API在线获取:就是你现在选的方式,优点是不用把几百张国旗打包进App,能大幅减小安装包体积,而且国旗数据可以动态更新(比如网站加了新国旗)。但依赖网络,没网的时候就用不了,适合想练习网络请求、后续打算扩展功能的情况。
- Firebase这类后端服务:如果以后想加用户排行榜、自定义题库、成绩同步这类功能,Firebase确实好用,但初期练手没必要急着上——先把核心的问答功能跑通,等需要扩展再考虑也不迟。
二、你的JSON解析问题怎么解决?
你现在的代码和目标JSON格式不匹配,所以解析会出问题。先看一下目标JSON的结构:它是一个[String: String]的字典,键是国旗的缩写(比如"ad"),值是国家名称(比如"Andorra")。我们可以直接把它解码成字典,再转换成自定义的Flag结构体。
第一步:简化Flag结构体
没必要把flagLink存在结构体里,用计算属性动态生成更合理,这样还能避免冗余数据:
import Foundation import SwiftUI struct Flag: Identifiable { let shortName: String // 国旗缩写,比如"ad" let name: String // 国家名称,比如"Andorra" let id = UUID() // 符合Identifiable协议的唯一标识 // 动态生成国旗图片链接 var flagLink: String { "https://flagcdn.com/w320/\(shortName).png" } }
第二步:修正JSON请求与解析逻辑
把原来的getJSON改成带回调的函数,方便在SwiftUI视图里处理返回的国旗数据:
func fetchFlags(completion: @escaping ([Flag]?) -> Void) { guard let url = URL(string: "https://flagcdn.com/fr/codes.json") else { print("无效的URL,请检查链接") completion(nil) return } URLSession.shared.dataTask(with: url) { data, response, error in // 先处理错误情况 if let error = error { print("网络请求出错:\(error.localizedDescription)") completion(nil) return } guard let data = data else { print("未获取到有效数据") completion(nil) return } do { // 直接解码成[String: String]类型的字典 let flagDictionary = try JSONDecoder().decode([String: String].self, from: data) // 把字典转换成Flag数组 let flags = flagDictionary.map { keyValue in Flag(shortName: keyValue.key, name: keyValue.value) } // 通过回调返回结果 completion(flags) } catch { print("JSON解析失败:\(error.localizedDescription)") completion(nil) } }.resume() }
第三步:在SwiftUI视图里使用
你可以用@State属性存储国旗数组,在视图出现时调用fetchFlags,然后随机选择国旗展示:
struct FlagQuizView: View { @State private var allFlags: [Flag] = [] @State private var currentFlag: Flag? var body: some View { VStack(spacing: 20) { // 展示当前选中的国旗 if let flag = currentFlag { AsyncImage(url: URL(string: flag.flagLink)) { image in image.resizable() .scaledToFit() .frame(height: 200) } placeholder: { ProgressView("加载中国旗...") } Text(flag.name) .font(.title) .bold() } else { Text("点击按钮开始游戏") .font(.headline) } // 随机选择国旗的按钮 Button("随机出题") { currentFlag = allFlags.randomElement() } .buttonStyle(.borderedProminent) .disabled(allFlags.isEmpty) // 没加载完数据时按钮禁用 } .padding() .onAppear { // 视图出现时获取国旗数据 fetchFlags { fetchedFlags in if let flags = fetchedFlags { // 回到主线程更新UI DispatchQueue.main.async { allFlags = flags } } } } } }
这样修改后,你就能成功获取国旗数据,随机展示国旗和对应的国家名称啦!
慢慢来,新手遇到JSON解析和网络请求的问题很正常,多试几次就能掌握啦~
备注:内容来源于stack exchange,提问作者axeeeeelt
相关产品推荐
相关产品推荐

