无法 return 或使用 JSONDecoder 解码数据
Can't return or decode data with JSONDecoder
我正在 Xcode 游乐场学习使用 JSON 解码数据,但无法弄清楚我的代码有什么问题,我无法 return 数据也无法解码.
这是我的代码:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
extension URL {
func withQueries(_ queries: [String: String]) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap { URLQueryItem(name: [=11=].0, value: [=11=].1) }
return components?.url
}
}
struct StoreItems: Codable {
let results: [StoreItem]
}
struct StoreItem: Codable {
var name: String
var artist: String
var kind: String
var artworkURL: URL
var description: String
enum CodingKeys: String, CodingKey {
case name = "trackName"
case artist = "artistName"
case kind
case artworkURL
case description
}
enum AdditionalKeys: String, CodingKey {
case longDescription
}
init(from decoder: Decoder) throws {
let valueContainer = try decoder.container(keyedBy: CodingKeys.self)
name = try valueContainer.decode(String.self, forKey: CodingKeys.name)
artist = try valueContainer.decode(String.self, forKey: CodingKeys.artist)
kind = try valueContainer.decode(String.self, forKey: CodingKeys.kind)
artworkURL = try valueContainer.decode(URL.self, forKey: CodingKeys.artworkURL)
if let description = try? valueContainer.decode(String.self, forKey: CodingKeys.description) {
self.description = description
} else {
let additionalValues = try decoder.container(keyedBy: AdditionalKeys.self)
description = (try? additionalValues.decode(String.self, forKey: AdditionalKeys.longDescription)) ?? ""
}
}
}
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?) -> Void) {
let baseURL = URL(string: "https://www.itunes.apple.com/search?")!
guard let url = baseURL.withQueries(query) else {
completion(nil)
print("Unable to build URL with supplied queries.")
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
let decoder = JSONDecoder()
if let data = data,
let storeItems = try? decoder.decode(StoreItems.self, from: data) {
completion(storeItems.results)
} else {
print("Either no data was returned or data was not properly decoded.")
completion(nil)
return
}
}
task.resume()
}
let query: [String: String] = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) { (items) in
print(items)
}
这是打印到控制台的内容,我猜这表明我的 "task":
有问题
没有数据被 returned 或数据没有正确解码。
无
这不是具体的解决方案,而是关于如何调试解码的建议。
用下面的代码替换整个以if let data = data,
开头的表达式并调试代码。从 dataTask
返回的潜在(在您的情况下是确定的)错误也得到处理。
guard let data = data else {
print(error!)
completion(nil)
return
}
do {
let storeItems = try decoder.decode(StoreItems.self, from: data)
completion(storeItems.results)
} catch DecodingError.dataCorrupted(let context) {
print(context.debugDescription)
} catch DecodingError.keyNotFound(let key, let context) {
print("Key '\(key)' not Found")
print("Debug Description:", context.debugDescription)
} catch DecodingError.valueNotFound(let value, let context) {
print("Value '\(value)' not Found")
print("Debug Description:", context.debugDescription)
} catch DecodingError.typeMismatch(let type, let context) {
print("Type '\(type)' mismatch")
print("Debug Description:", context.debugDescription)
} catch {
print("error: ", error)
}
completion(nil)
几个问题:
- URL 是错误的。没有
www
.
artworkURL
似乎是可选的,因为您的搜索没有 return 该键的值。
当我修复这些问题时,它起作用了:
extension URL {
func withQueries(_ queries: [String: String]) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap { URLQueryItem(name: [=10=].0, value: [=10=].1) }
return components?.url
}
}
struct StoreItems: Codable {
let results: [StoreItem]
}
struct StoreItem: Codable {
var name: String
var artist: String
var kind: String
var artworkURL: URL?
var shortDescription: String?
var longDescription: String?
enum CodingKeys: String, CodingKey {
case name = "trackName"
case artist = "artistName"
case kind, artworkURL, shortDescription, longDescription
}
}
enum FetchError: Error {
case urlError
case unknownNetworkError
}
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?, Error?) -> Void) {
let baseURL = URL(string: "https://itunes.apple.com/search")!
guard let url = baseURL.withQueries(query) else {
completion(nil, FetchError.urlError)
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil, error ?? FetchError.unknownNetworkError)
return
}
do {
let storeItems = try JSONDecoder().decode(StoreItems.self, from: data)
completion(storeItems.results, nil)
} catch let parseError {
completion(nil, parseError)
}
}
task.resume()
}
并且:
let query = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) { items, error in
guard let items = items, error == nil else {
print(error ?? "Unknown error")
return
}
print(items)
}
注意,我建议您将错误添加到完成处理程序中,以便您可以看到 为什么 它失败了(在您的情况下,第一个问题是 URL 错了)。
我正在 Xcode 游乐场学习使用 JSON 解码数据,但无法弄清楚我的代码有什么问题,我无法 return 数据也无法解码. 这是我的代码:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
extension URL {
func withQueries(_ queries: [String: String]) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap { URLQueryItem(name: [=11=].0, value: [=11=].1) }
return components?.url
}
}
struct StoreItems: Codable {
let results: [StoreItem]
}
struct StoreItem: Codable {
var name: String
var artist: String
var kind: String
var artworkURL: URL
var description: String
enum CodingKeys: String, CodingKey {
case name = "trackName"
case artist = "artistName"
case kind
case artworkURL
case description
}
enum AdditionalKeys: String, CodingKey {
case longDescription
}
init(from decoder: Decoder) throws {
let valueContainer = try decoder.container(keyedBy: CodingKeys.self)
name = try valueContainer.decode(String.self, forKey: CodingKeys.name)
artist = try valueContainer.decode(String.self, forKey: CodingKeys.artist)
kind = try valueContainer.decode(String.self, forKey: CodingKeys.kind)
artworkURL = try valueContainer.decode(URL.self, forKey: CodingKeys.artworkURL)
if let description = try? valueContainer.decode(String.self, forKey: CodingKeys.description) {
self.description = description
} else {
let additionalValues = try decoder.container(keyedBy: AdditionalKeys.self)
description = (try? additionalValues.decode(String.self, forKey: AdditionalKeys.longDescription)) ?? ""
}
}
}
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?) -> Void) {
let baseURL = URL(string: "https://www.itunes.apple.com/search?")!
guard let url = baseURL.withQueries(query) else {
completion(nil)
print("Unable to build URL with supplied queries.")
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
let decoder = JSONDecoder()
if let data = data,
let storeItems = try? decoder.decode(StoreItems.self, from: data) {
completion(storeItems.results)
} else {
print("Either no data was returned or data was not properly decoded.")
completion(nil)
return
}
}
task.resume()
}
let query: [String: String] = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) { (items) in
print(items)
}
这是打印到控制台的内容,我猜这表明我的 "task":
有问题没有数据被 returned 或数据没有正确解码。
无
这不是具体的解决方案,而是关于如何调试解码的建议。
用下面的代码替换整个以if let data = data,
开头的表达式并调试代码。从 dataTask
返回的潜在(在您的情况下是确定的)错误也得到处理。
guard let data = data else {
print(error!)
completion(nil)
return
}
do {
let storeItems = try decoder.decode(StoreItems.self, from: data)
completion(storeItems.results)
} catch DecodingError.dataCorrupted(let context) {
print(context.debugDescription)
} catch DecodingError.keyNotFound(let key, let context) {
print("Key '\(key)' not Found")
print("Debug Description:", context.debugDescription)
} catch DecodingError.valueNotFound(let value, let context) {
print("Value '\(value)' not Found")
print("Debug Description:", context.debugDescription)
} catch DecodingError.typeMismatch(let type, let context) {
print("Type '\(type)' mismatch")
print("Debug Description:", context.debugDescription)
} catch {
print("error: ", error)
}
completion(nil)
几个问题:
- URL 是错误的。没有
www
. artworkURL
似乎是可选的,因为您的搜索没有 return 该键的值。
当我修复这些问题时,它起作用了:
extension URL {
func withQueries(_ queries: [String: String]) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap { URLQueryItem(name: [=10=].0, value: [=10=].1) }
return components?.url
}
}
struct StoreItems: Codable {
let results: [StoreItem]
}
struct StoreItem: Codable {
var name: String
var artist: String
var kind: String
var artworkURL: URL?
var shortDescription: String?
var longDescription: String?
enum CodingKeys: String, CodingKey {
case name = "trackName"
case artist = "artistName"
case kind, artworkURL, shortDescription, longDescription
}
}
enum FetchError: Error {
case urlError
case unknownNetworkError
}
func fetchItems(matching query: [String: String], completion: @escaping ([StoreItem]?, Error?) -> Void) {
let baseURL = URL(string: "https://itunes.apple.com/search")!
guard let url = baseURL.withQueries(query) else {
completion(nil, FetchError.urlError)
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil, error ?? FetchError.unknownNetworkError)
return
}
do {
let storeItems = try JSONDecoder().decode(StoreItems.self, from: data)
completion(storeItems.results, nil)
} catch let parseError {
completion(nil, parseError)
}
}
task.resume()
}
并且:
let query = [
"term": "Inside Out 2015",
"media": "movie",
"lang": "en_us",
"limit": "10"
]
fetchItems(matching: query) { items, error in
guard let items = items, error == nil else {
print(error ?? "Unknown error")
return
}
print(items)
}
注意,我建议您将错误添加到完成处理程序中,以便您可以看到 为什么 它失败了(在您的情况下,第一个问题是 URL 错了)。