2025-02-10 11:30:27
,某些文章具有时效性,若有错误或已失效,请在下方留言。Fetching JSON from the network and using Codable
to convert it into native Swift objects is probably the most common task for any Swift developer, usually followed by displaying that data in a List
or UITableView
depending on whether they are using SwiftUI or UIKit.
从网络获取 JSON 并使用 Codable
将其转换为原生 Swift 对象可能是任何 Swift 开发人员最常见的任务,通常随后在 List
或 UITableView
中显示该数据,具体取决于他们使用的是 SwiftUI 还是 UIKit。
Well, using Swift’s concurrency features we can write a small but beautiful extension for URLSession
that makes such work just a single line of code – you just tell iOS what data type to expect and the URL to fetch, and it will do the rest.
好吧,使用 Swift 的并发功能,我们可以为 URLSession
编写一个小而漂亮的扩展,它使这样的工作只需一行代码 – 您只需告诉 iOS 需要什么数据类型和要获取的 URL,剩下的工作就会交给它。
To add some extra flexibility, we can also provide options to customize decoding strategies for keys, data, and dates, providing sensible defaults for each one to keep our call sites clear for the most common usages.
为了增加一些额外的灵活性,我们还可以提供选项来自定义键、数据和日期的解码策略,为每个策略提供合理的默认值,以保持我们的调用站点对于最常见的用途清晰。
Here’s how it’s done: 这是如何完成的:
extension URLSession {
func decode<T: Decodable>(
_ type: T.Type = T.self,
from url: URL,
keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys,
dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .deferredToData,
dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate
) async throws -> T {
let (data, _) = try await data(from: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = keyDecodingStrategy
decoder.dataDecodingStrategy = dataDecodingStrategy
decoder.dateDecodingStrategy = dateDecodingStrategy
let decoded = try decoder.decode(T.self, from: data)
return decoded
}
}
That does several things:
这有几件事:
- It’s an extension on
URLSession
, so you can go ahead and create your own custom session with a unique configuration if needed.
它是URLSession
的扩展,因此如果需要,您可以继续使用唯一配置创建自己的自定义会话。 - It uses generics, so that it will work with anything that conforms to the
Decodable
protocol – that’s half ofCodable
, so if you useCodable
it will work there too.
它使用泛型,因此它可以与任何符合Decodable
协议的东西一起工作——这是Codable
的一半,所以如果你使用Codable
,它也可以在那里工作。 - It uses
T.self
for the default data type, so if Swift can infer your type then you don’t need to repeat yourself.
它使用T.self
作为默认数据类型,因此如果 Swift 可以推断出您的类型,那么您就不需要重复自己。 - It allows all errors to propagate to your call site, so you can handle networking and/or decoding errors as needed.
它允许所有错误传播到您的调用站点,因此您可以根据需要处理网络和/或解码错误。
To use the extension in your own code, first define a type you want to work with, then go ahead and call decode()
in whichever way you need:
要在你自己的代码中使用扩展,首先定义你想要使用的类型,然后以你需要的任何方式调用 decode()
:
extension URLSession {
func decode<T: Decodable>(
_ type: T.Type = T.self,
from url: URL,
keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys,
dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .deferredToData,
dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate
) async throws -> T {
let (data, _) = try await data(from: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = keyDecodingStrategy
decoder.dataDecodingStrategy = dataDecodingStrategy
decoder.dateDecodingStrategy = dateDecodingStrategy
let decoded = try decoder.decode(T.self, from: data)
return decoded
}
}
struct User: Codable {
let id: UUID
let name: String
let age: Int
}
struct Message: Codable {
let id: Int
let user: String
let text: String
}
do {
// Fetch and decode a specific type
let url1 = URL(string: "https://hws.dev/user-24601.json")!
let user = try await URLSession.shared.decode(User.self, from: url1)
print("Downloaded \(user.name)")
// Infer the type because Swift has a type annotation
let url2 = URL(string: "https://hws.dev/inbox.json")!
let messages: [Message] = try await URLSession.shared.decode(from: url2)
print("Downloaded \(messages.count) messages")
// Create a custom URLSession and decode a Double array from that
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
let session = URLSession(configuration: config)
let url3 = URL(string: "https://hws.dev/readings.json")!
let readings = try await session.decode([Double].self, from: url3)
print("Downloaded \(readings.count) readings")
} catch {
print("Download error: \(error.localizedDescription)")
}
As you can see, with that small extension in place it becomes trivial to fetch and decode any type of Codable
data with just one line of Swift.
正如你所看到的,有了这个小的扩展,只用一行 Swift 就可以很容易地获取和解码任何类型的 Codable
数据。