如何在Swift中调用GitHub API实现macOS应用版本更新检测?
问题根源
你遇到的报错已经明确指向核心问题:你将GitHub API返回的单个字典对象错误当作数组进行解码。你调用的/repos/{owner}/{repo}/releases/latest接口返回的是单个Release信息结构体,不是Release数组,因此用[TaskEntry].self作为解码类型必然触发类型不匹配错误。
修复步骤
1. 调整数据模型与状态变量
将原TaskEntry替换为语义更清晰的GitHubRelease模型,同时把存储结果的状态变量从数组改为单个可选对象:Model.swift代码:
import Foundation struct GitHubRelease: Codable { let id: Int let tag_name: String let name: String // 后续需要更新描述、安装包下载链接等字段,只要和GitHub返回字段名对应即可继续扩展 }
ContentView.swift中状态变量修改为:
@State var latestRelease: GitHubRelease?
2. 优化请求与解码逻辑
- 移除不安全的
try!,改用do-catch捕获解码错误,避免App直接崩溃 - GitHub API要求必须携带
User-Agent请求头,否则会返回403拒绝访问 - 解码类型改为单个
GitHubRelease.self而非数组类型
完整loadData方法代码:
func loadData() { guard let url = URL(string: "https://api.github.com/repos/NCX-Programming/RNGTool/releases/latest") else { print("无效URL") return } var request = URLRequest(url: url) // 这里替换为你App的Bundle ID即可 request.setValue("your_app_bundle_id", forHTTPHeaderField: "User-Agent") URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("请求出错: \(error.localizedDescription)") return } guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { print("响应无效") return } guard let data = data else { print("返回数据为空") return } do { let release = try JSONDecoder().decode(GitHubRelease.self, from: data) DispatchQueue.main.async { self.latestRelease = release // 此处可添加版本对比逻辑:取出release.tag_name和本地App版本对比即可 // 本地版本号获取方法:Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String } } catch { print("解码失败: \(error)") // 调试时可打开下面的注释打印返回的原始JSON内容排查问题 // if let jsonStr = String(data: data, encoding: .utf8) { // print("返回JSON: \(jsonStr)") // } } }.resume() }
3. 调整View渲染逻辑
因为现在存储的是单个可选的版本信息,不用List布局,直接判断变量是否存在即可渲染:
var body: some View { VStack(alignment: .leading, spacing: 12) { if let latest = latestRelease { Text("最新版本号: \(latest.tag_name)") Text("版本名称: \(latest.name)") // 此处可添加更新提示弹窗、下载按钮等业务逻辑 } else { Text("正在检查更新...") } } .padding() .onAppear(perform: loadData) }
额外注意事项
- GitHub API对未认证的请求有每小时60次的速率限制,个人自用的更新检测场景完全够用,若需要更高请求配额可以给请求添加
Authorization头携带个人访问令牌 - 版本号对比可以使用
String的compare(_:options:)方法,传入.numeric参数即可按照数值规则正确对比版本号大小,比如正确识别1.2.0比1.1.9版本更高
内容的提问来源于stack exchange,提问作者NinjaCheetah
相关产品推荐
相关产品推荐

