我想在我的macOS SwiftUI应用程序中通过API从GitHub提取最新版本,然后比较标签,从而创建一个更新检测系统。我如何从Swift访问API ?我试过这里,medium.com,这里,swifttom.com和这里,steveclarkapps.com的方法,但没有一个达到我想要做的。
对于第一个方法,代码与提供的示例API一起工作,但不与GitHub API一起工作,它返回这个错误:
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
方法2也有同样的问题。
我甚至不能得到足够的方法3的代码工作来尝试它。
下面是我根据medium.com方法改编的代码:
Model.swift
import Foundation
struct TaskEntry: Codable {
let id: Int
let tag_name: String
let name: String
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@State var results = [TaskEntry]()
var body: some View {
List(results, id: .id) { item in
VStack(alignment: .leading) {
Text(item.name)
}
}.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://api.github.com/repos/NCX-Programming/RNGTool/releases/latest") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
/*if*/ let response = try! JSONDecoder().decode([TaskEntry].self, from: data) /*{*/
DispatchQueue.main.async {
self.results = response
}
return
/*}*/
}
}.resume()
}
}
注释掉的代码和看起来不相关的变量名只是剩下的。
OS:macOS Big Sur 11.6Xcode版本:13.0
在浏览器中打开:
https://api.github.com/repos/NCX-Programming/RNGTool/releases/latest
你会注意到它不是一个数组而是一个对象。你应该像这样解码一个对象:
JSONDecoder().decode(TaskEntry.self, from: data)
编辑:
这要求您更改视图。注意,这不再是List
,因为您不再获取一个数组,而是获取单个项:
struct TaskEntry: Codable {
let id: Int
let tagName: String
let name: String
}
struct ContentView: View {
@State var entry: TaskEntry? = nil
var body: some View {
VStack(alignment: .leading) {
if let entry = entry {
Text("(entry.id)")
Text(entry.name)
Text(entry.tagName)
} else {
ProgressView()
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://api.github.com/repos/NCX-Programming/RNGTool/releases/latest") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
// TODO: Handle data task error
return
}
guard let data = data else {
// TODO: Handle this
return
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let response = try decoder.decode(TaskEntry.self, from: data)
DispatchQueue.main.async {
self.entry = response
}
} catch {
// TODO: Handle decoding error
print(error)
}
}.resume()
}
}
注意:我也做了一些其他的改进
- 使用JSONDecoder从蛇形转换成驼形
- 添加了do catch块,这样你的应用程序就不会崩溃
- 解码前检查错误
- 添加加载指示符(必须在else中添加某些内容) 然而,
根据我们的讨论,您可能打错了端点。端点返回的不是数组而是单个对象,因为JSON响应以{
而不是[
开头
我已经调整了我的回答,以改变我认为你应该打电话的端点:
struct TaskEntry: Codable {
let id: Int
let tagName: String
let name: String
}
struct ContentView: View {
@State var results: [TaskEntry]? = nil
var body: some View {
if let results = results {
List(results, id: .id) { item in
VStack(alignment: .leading) {
Text(item.name)
}
}
} else {
VStack(alignment: .leading) {
ProgressView()
.onAppear(perform: loadData)
}
}
}
func loadData() {
guard let url = URL(string: "https://api.github.com/repos/NCX-Programming/RNGTool/releases") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
// TODO: Handle data task error
return
}
guard let data = data else {
// TODO: Handle this
return
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let response = try decoder.decode([TaskEntry].self, from: data)
DispatchQueue.main.async {
self.results = response
}
} catch {
// TODO: Handle decoding error
print(error)
}
}.resume()
}
}