Bei*_*Bei 0 api json nested object swiftui
初学者,对此有点难以理解。;)
我找到了一些示例,这些示例向我展示了如何从 JSON API feed 中获取数据(如果 feed 的结构为对象数组),但我不知道如何在以下情况下获取数据(特别是url和title) :我正在检索的数据以更复杂的嵌套结构返回,如下所示:
{
"races": {
"videos": [{
"id": 1,
"url": "firsturl",
"title": "1st Video Title"
}, {
"id": 2,
"url": "secondurl",
"title": "2nd Video Title"
}]
}
}
Run Code Online (Sandbox Code Playgroud)
我已经成功地从另一个 API 提要中获取数据,该提要的结构为一个简单的对象数组——它类似于上面的内容,但没有额外的两个导入对象,即: { "races": { "videos":
这是我从几个适用于简单数组的示例中拼凑而成的代码:
import SwiftUI
struct Video: Codable, Identifiable {
public var id: Int
public var url: String
public var title: String
}
class Videos: ObservableObject {
@Published var videos = [Video]()
init() {
let url = URL(string: "https://exampledomain.com/jsonapi")!
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let videoData = data {
let decodedData = try JSONDecoder().decode([Video].self, from: videoData)
DispatchQueue.main.async {
self.videos = decodedData
}
} else {
print("no data found")
}
} catch {
print("an error occurred")
}
}.resume()
}
}
struct VideosView: View {
@ObservedObject var fetch = Videos()
var body: some View {
VStack {
List(fetch.videos) { video in
VStack(alignment: .leading) {
Text(video.title)
Text("\(video.url)")
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我花了几天的时间阅读和观看教程,但到目前为止,没有任何东西可以帮助我处理更复杂的 JSON API feed。任何提示将非常感谢!
更新:
在 Swift Playground 教程和下面评论中提到的建议结构的帮助下,我成功地检索了更复杂的数据,但仅在 Swift Playgrounds 中使用:
import SwiftUI
struct Welcome: Codable {
let races: Races
}
struct Races: Codable {
let videos: [Video]
}
struct Video: Codable {
let id: Int
let url, title: String
}
func getJSON<T: Decodable>(urlString: String, completion: @escaping (T?) -> Void) {
guard let url = URL(string: urlString) else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
return
}
guard let data = data else {
completion(nil)
return
}
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(T.self, from: data) else {
completion(nil)
return
}
completion(decodedData)
}.resume()
}
getJSON(urlString: "https://not-the-real-domain.123/api/") { (followers:Welcome?) in
if let followers = followers {
for result in followers.races.videos {
print(result.title )
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我正在努力解决如何将此 Playgrounds 片段正确集成到工作 SwiftUI 文件的 VideosViews 等中。
更新2:
import SwiftUI
struct Welcome: Codable {
let races: RaceItem
}
struct RaceItem: Codable {
let videos: [VideoItem]
}
struct VideoItem: Codable {
let id: Int
let url: String
let title: String
}
class Fetcher: ObservableObject {
func getJSON<T: Decodable>(urlString: String, completion: @escaping (T?) -> Void) {
guard let url = URL(string: urlString) else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
return
}
guard let data = data else {
completion(nil)
return
}
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(T.self, from: data) else {
completion(nil)
return
}
completion(decodedData)
}.resume()
}
}
struct JSONRacesView: View {
@ObservedObject var fetch = Fetcher()
getJSON(urlString:"https://not-the-real-domain.123/api/") { (followers:Welcome?) in
if let followers = followers {
for result in followers.races.videos {
print(result.title )
}
}
}
var body: some View {
VStack {
List(fetch.tracks) { track in
VStack(alignment: .leading) {
Text(track.title)
Text("\(track.url)")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有一个名为 QuickType (app.quicktype.io) 的很棒的网站,您可以在其中粘贴一些 JSON 并获取为您生成的 Swift 结构。这是它为您提供的:
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let races: Races
}
// MARK: - Races
struct Races: Codable {
let videos: [Video]
}
// MARK: - Video
struct Video: Codable {
let id: Int
let url, title: String
}
Run Code Online (Sandbox Code Playgroud)
他们的模板生成器中有一个错误,该错误破坏了演示行(我已经提交了一个已合并的拉取请求,但在撰写本文时尚未在网站上发布),但它应该是这样的:
let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
Run Code Online (Sandbox Code Playgroud)
使用do/try
这样您可以捕获错误,您可以通过执行以下操作来解码数据并到达较低级别:
do {
let welcome = try JSONDecoder().decode(Welcome.self, from: jsonData)
let videos = welcome.races.videos //<-- this ends up being your [Video] array
} catch {
//handle any errors
}
Run Code Online (Sandbox Code Playgroud)
根据您的评论和更新进行更新:您选择的路线与我最初的建议略有不同,但这很好。我唯一建议的是,您可能希望在某个时刻处理处理错误,而不是仅仅返回nil
所有完成结果(假设您需要处理错误 - 也许只是不加载是可以接受的)。
这是代码的简单重构:
class Fetcher: ObservableObject {
@Published var tracks : [VideoItem] = []
private func getJSON<T: Decodable>(urlString: String, completion: @escaping (T?) -> Void) {
guard let url = URL(string: urlString) else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
return
}
guard let data = data else {
completion(nil)
return
}
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode(T.self, from: data) else {
completion(nil)
return
}
completion(decodedData)
}.resume()
}
func fetchData() {
getJSON(urlString:"https://not-the-real-domain.123/api/") { (followers:Welcome?) in
DispatchQueue.main.async {
self.tracks = followers?.races.videos ?? []
}
}
}
}
struct JSONRacesView: View {
@StateObject var fetch = Fetcher()
var body: some View {
VStack {
List(fetch.tracks, id: \.id) { track in
VStack(alignment: .leading) {
Text(track.title)
Text("\(track.url)")
}
}
}.onAppear {
fetch.fetchData()
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以看到现在Fetcher
有一个@Published
用于存储曲目的属性 ([VideoItem])。getJSON
仍然在 fetcher 中,但现在private
只是为了表明它不应该被直接调用。但是,现在有一个名为fetchData()
您的视图将调用的新函数。当fetchData
获取数据时,它将 @Published 属性设置为该数据。我使用??
运算符告诉编译器,如果followers
为 nil,则直接使用[]
即可。这一切都在一个DispatchQueue.main.async
块中,因为 URL 调用可能不会在主线程上返回,并且我们需要确保始终更新主线程上的 UI(如果您更新 UI,Xcode 会在运行时警告您这一点)不同的线程)。
JSONRacesView
呼入,这恰好fetchData
在onAppear
听起来会发生的时候发生。
最后要注意的是我用的@StateObject
是@ObservedObject
. 如果您尚未使用 iOS 14 或 macOS 11,则可以改用 @ObservedObject。有一些差异超出了这个答案的范围,但很容易通过谷歌搜索到。