提问人:stoney 提问时间:11/17/2023 更新时间:11/17/2023 访问量:78
这个数据模型可以嵌套吗?
Can this data model be nested?
问:
这个数据模型可以嵌套吗,我已经试过了 很多事情,我无法让它工作。
struct Response: Codable {
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
}
var results: [Result]
}
这行不通,有办法吗?
供参考: 这个例子来自保罗·哈德森 视频教程。
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")
}
}
}
我想不出更多的细节。我只是 在这一点上不明白这一点。
答:
0赞
Rob
11/17/2023
#1
正如 workingdog 所说,您可以嵌套类型,但您的属性必须反映完全限定的类型名称,现在: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中显示一些用户友好的东西等),但这可能超出了这个问题的范围。
最后,虽然您可以将嵌套类型嵌入到对象中,但它回避了是否应该这样做的问题。这是两种不同类型的事物。 是一个 API 结构。数组的类型是模型类型(“曲目”、“专辑”等)。我不建议把它们纠缠在一起。Response
Response
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)
}
}
评论
0赞
stoney
11/17/2023
我喜欢对 do-catch、try-.decode 内容的改进。非常!对我有帮助。我之前尝试在 .decode 中使用 Response.Result,但没有用。Response.self 在 .decode 中找到 Result 结构,但它在另一个方向上不起作用。我是一个初学者,我被卡住了。哇,评论非常有帮助。
0赞
Rob
11/17/2023
是的,is 仅在 的声明中,但 仍然是 ,如上面的第一个代码片段所示。Response.Result
var results = [Response.Result]()
decode
Response.self
评论
ContentView
@State private var results = [Response.Result]()
try?
print("Invalid Data")
print("Invalid Data \(error)")