这个数据模型可以嵌套吗,我试过了 很多事情,但我无法让它发挥作用。
struct Response: Codable {
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
}
var results: [Result]
}
这不行,有办法吗?
供参考: 这个例子来自 Paul Hudson 视频教程。
import SwiftUI
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
}
struct ContentView: View {
@State private var results = [Result]()
var body: some View {
List {
ForEach(results, id: \.trackId) { item in
VStack(alignment: .leading) {
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
.font(.caption)
}
}
}
.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song&limit=4") else {
print("Invalid URL")
return }
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
results = decodedResponse.results
}
} catch {
print("Invalid Data")
}
}
}
我想不出更多细节了。我只是 暂时不明白这一点。
正如工作狗所说,您可以嵌套类型,但您的属性必须反映完全限定的类型名称,现在
Response.Result
:
@State private var results = [Response.Result]()
无关,但我也建议:
try?
(因为如果失败了,你就会默默地忽略这个问题);let
而不是 var
,因为推理不可变对象总是比推理可变对象更容易……仅在需要可变属性时使用 var
;和例如
struct Response: Codable {
let results: [Result] // use `let` instead of `var`, unless you really need mutability
struct Result: Codable {
let trackId: Int
let trackName: String
let collectionName: String
}
}
struct ContentView: View {
@State private var results = [Response.Result]() // `Response.Result`
var body: some View {
List {
ForEach(results, id: \.trackId) { item in
VStack(alignment: .leading) {
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
.font(.caption)
}
}
}
.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song&limit=4") else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
results = try JSONDecoder() // use `try`, not `try?`
.decode(Response.self, from: data)
.results
} catch {
print("Invalid Data", error) // include the error, so if it failed, you can meaningfully diagnose what went wrong
}
}
}
还有很多其他潜在的改进(例如,如果出现错误,在 UI 中显示一些用户友好的内容等),但这可能超出了这个问题的范围。
作为最后的观察,虽然您可以将嵌套类型嵌入到
Response
对象中,但它引出了一个问题:您是否应该。这是两种不同类型的事物。 Response
是一个API结构。 results
数组的类型是模型类型(“曲目”、“专辑”等)。我不建议将它们纠缠在一起。
例如,此 API 遵循一致的
{"results":[…]}
约定,其中该数组内的对象类型将根据请求的 entity
参数而变化。
因此需要不将子类型嵌入到
Response
中。就我个人而言,我会使用通用模式:
struct Track: Codable {
let trackId: Int
let trackName: String
let collectionName: String
}
struct Album: Codable {
let albumId: Int
let albumName: String
enum CodingKeys: String, CodingKey {
case albumId = "collectionId"
case albumName = "collectionName"
}
}
struct Response<T: Codable>: Codable {
let results: T
}
因此请求相册的函数可以解析
Response<Track>
,但是获取相册的端点可以解析 Response<Album>
。等等
恕我直言,这些模型对象(
Track
、Album
等)不属于Response
类型。
然后你可以做这样的事情:
@State private var tracks = [Track]()
func loadData() async {
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song&limit=4") else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
tracks = try JSONDecoder()
.decode(Response.self, from: data) // it infers the generic for us
.results
} catch {
print("Invalid Data", error)
}
}