如何在Swift中访问GitHub API ?



我想在我的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()
}
}

注意:我也做了一些其他的改进

  1. 使用JSONDecoder从蛇形转换成驼形
  2. 添加了do catch块,这样你的应用程序就不会崩溃
  3. 解码前检查错误
  4. 添加加载指示符(必须在else中添加某些内容)
  5. 然而,

根据我们的讨论,您可能打错了端点。端点返回的不是数组而是单个对象,因为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()
}
}