Swift 4 个 Codable 数组
Swift 4 Codable Array's
所以我有一个 API 路由 returns 一个 JSON 对象数组。例如:
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
我正在尝试设想如何使用 Swift 中的新可编码功能将它们转换为 class 中的两个对象。因此,如果我有一个人 class 是可编码的,我想接受该响应并让它给我两个人对象。
我也在使用 Alamofire 来处理请求。
我该怎么做?到目前为止,我所看到的与可编码内容相关的所有内容都只允许 1 个对象。而且我还没有看到与 Alamofire 或网络框架的任何集成。
关于 Alamofire 5 的更新:responseJSONDecodable
。
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
Alamofire.request(request).responseJSONDecodable { (response: DataResponse<Person>) in
print(response)
}
Alamofire 4 暂时不会添加 Codable 支持(参见 #2177), you can use this extension instead: https://github.com/Otbivnoe/CodableAlamofire。
let jsonData = """
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
""".data(using: .utf8)!
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
let decoded = try! JSONDecoder().decode([Person].self, from: jsonData)
样本:http://swift.sandbox.bluemix.net/#/repl/59a4b4fad129044611590820
使用 CodableAlamofire:
let decoder = JSONDecoder()
Alamofire.request(url).responseDecodableObject(keyPath: nil, decoder: decoder) { (response: DataResponse<[Person]>) in
let persons = response.result.value
print(persons)
}
keypath
对应于JSON结构中包含结果的路径。例如:
{
"result": {
"persons": [
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
}
}
keypath
=> results.persons
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
keypath
=> nil
(空keypath
抛出异常)
我设法序列化了对可编码对象的数据响应。
例如,您可能已经熟悉转换 json 对象 [String: String]
。 json 对象需要使用 json.data(using: .utf8)!
.
转换为 Data
使用 Alamofire,很容易获得该数据(或者至少这种数据对我有用,已经与 .utf8
兼容),我可以使用这个已经可用的函数
func responseData(queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Data>) -> Void) -> Self
然后只需将该数据用作 completionHandler
中 Decoder
的输入
let objek = try JSONDecoder().decode(T.self, from: data)
您还可以通过文档
对一些通用序列化函数进行一些调整,
Generic Response Object Serialization
对此修改
func responseCodable<T: Codable>(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<T>) -> Void)
-> Self
{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else { return .failure(BackendError.network(error: error!)) }
guard let data = data else {
return .failure(BackendError.objectSerialization(reason: "data is not valid"))
}
do{
let objek = try JSONDecoder().decode(T.self, from: data!)
return .success(objek)
} catch let e {
return .failure(BackendError.codableSerialization(error: e))
}
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
示例结构
struct Fids: Codable {
var Status: Status?
var Airport: Airport?
var Record: [FidsRecord]
}
这样使用函数
Alamofire.request("http://whatever.com/zzz").responseCodable { (response: DataResponse<Fids>) in
switch response.result{
case .success(let value):
print(value.Airport)
// MARK: do whatever you want
case .failure(let error):
print(error)
self.showToast(message: error.localizedDescription)
}
}
为了解码为数组,为清楚起见,您在类型别名中做出响应:
typealias ServiceResponseObject = [ResponseObject]
但是你必须确认 Array to codable:
extension Array: Decodable where Element: Decodable {}
这应该使一切正常。
Swift 5 使用 Codable
Alamofire Generic Response
PersonModel.swift
(使用 SwiftyJsonAccelerator 创建)
import Foundation
class PersonModel: Codable {
enum CodingKeys: String, CodingKey {
case age
case firstname
case lastname }
var age: Int? var firstname: String? var lastname: String?
init (age: Int?, firstname: String?, lastname: String?) {
self.age = age
self.firstname = firstname
self.lastname = lastname }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
age = try container.decodeIfPresent(Int.self, forKey: .age)
firstname = try container.decodeIfPresent(String.self, forKey: .firstname)
lastname = try container.decodeIfPresent(String.self, forKey: .lastname) }
}
通用获取响应
func genericGET<T: Decodable>(urlString: String, completion: @escaping (T?) -> ()) {
Alamofire.request(urlString)
.responseJSON { response in
// check for errors
switch response.result {
case .success(_):
do {
let obj = try JSONDecoder().decode(T.self, from: response.data!)
completion(obj)
} catch let jsonErr {
print("Failed to decode json:", jsonErr)
}
break
case .failure(_):
completion(nil)
break
}
}
}
调用这个方法
genericGET(urlString: "YOUR_URL") { (persons: [PersonModel]?) in
print(persons?[0].firstname)
}
所以我有一个 API 路由 returns 一个 JSON 对象数组。例如:
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
我正在尝试设想如何使用 Swift 中的新可编码功能将它们转换为 class 中的两个对象。因此,如果我有一个人 class 是可编码的,我想接受该响应并让它给我两个人对象。
我也在使用 Alamofire 来处理请求。
我该怎么做?到目前为止,我所看到的与可编码内容相关的所有内容都只允许 1 个对象。而且我还没有看到与 Alamofire 或网络框架的任何集成。
关于 Alamofire 5 的更新:responseJSONDecodable
。
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
Alamofire.request(request).responseJSONDecodable { (response: DataResponse<Person>) in
print(response)
}
Alamofire 4 暂时不会添加 Codable 支持(参见 #2177), you can use this extension instead: https://github.com/Otbivnoe/CodableAlamofire。
let jsonData = """
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
""".data(using: .utf8)!
struct Person: Codable {
let firstName, lastName: String
let age: Int
enum CodingKeys : String, CodingKey {
case firstName = "firstname"
case lastName = "lastname"
case age
}
}
let decoded = try! JSONDecoder().decode([Person].self, from: jsonData)
样本:http://swift.sandbox.bluemix.net/#/repl/59a4b4fad129044611590820
使用 CodableAlamofire:
let decoder = JSONDecoder()
Alamofire.request(url).responseDecodableObject(keyPath: nil, decoder: decoder) { (response: DataResponse<[Person]>) in
let persons = response.result.value
print(persons)
}
keypath
对应于JSON结构中包含结果的路径。例如:
{
"result": {
"persons": [
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
}
}
keypath
=> results.persons
[
{"firstname": "Tom", "lastname": "Smith", "age": 31},
{"firstname": "Bob", "lastname": "Smith", "age": 28}
]
keypath
=> nil
(空keypath
抛出异常)
我设法序列化了对可编码对象的数据响应。
例如,您可能已经熟悉转换 json 对象 [String: String]
。 json 对象需要使用 json.data(using: .utf8)!
.
Data
使用 Alamofire,很容易获得该数据(或者至少这种数据对我有用,已经与 .utf8
兼容),我可以使用这个已经可用的函数
func responseData(queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Data>) -> Void) -> Self
然后只需将该数据用作 completionHandler
Decoder
的输入
let objek = try JSONDecoder().decode(T.self, from: data)
您还可以通过文档
对一些通用序列化函数进行一些调整,Generic Response Object Serialization
对此修改
func responseCodable<T: Codable>(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<T>) -> Void)
-> Self
{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else { return .failure(BackendError.network(error: error!)) }
guard let data = data else {
return .failure(BackendError.objectSerialization(reason: "data is not valid"))
}
do{
let objek = try JSONDecoder().decode(T.self, from: data!)
return .success(objek)
} catch let e {
return .failure(BackendError.codableSerialization(error: e))
}
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
示例结构
struct Fids: Codable {
var Status: Status?
var Airport: Airport?
var Record: [FidsRecord]
}
这样使用函数
Alamofire.request("http://whatever.com/zzz").responseCodable { (response: DataResponse<Fids>) in
switch response.result{
case .success(let value):
print(value.Airport)
// MARK: do whatever you want
case .failure(let error):
print(error)
self.showToast(message: error.localizedDescription)
}
}
为了解码为数组,为清楚起见,您在类型别名中做出响应:
typealias ServiceResponseObject = [ResponseObject]
但是你必须确认 Array to codable:
extension Array: Decodable where Element: Decodable {}
这应该使一切正常。
Swift 5 使用 Codable
Alamofire Generic Response
PersonModel.swift (使用 SwiftyJsonAccelerator 创建)
import Foundation
class PersonModel: Codable {
enum CodingKeys: String, CodingKey {
case age
case firstname
case lastname }
var age: Int? var firstname: String? var lastname: String?
init (age: Int?, firstname: String?, lastname: String?) {
self.age = age
self.firstname = firstname
self.lastname = lastname }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
age = try container.decodeIfPresent(Int.self, forKey: .age)
firstname = try container.decodeIfPresent(String.self, forKey: .firstname)
lastname = try container.decodeIfPresent(String.self, forKey: .lastname) }
}
通用获取响应
func genericGET<T: Decodable>(urlString: String, completion: @escaping (T?) -> ()) {
Alamofire.request(urlString)
.responseJSON { response in
// check for errors
switch response.result {
case .success(_):
do {
let obj = try JSONDecoder().decode(T.self, from: response.data!)
completion(obj)
} catch let jsonErr {
print("Failed to decode json:", jsonErr)
}
break
case .failure(_):
completion(nil)
break
}
}
}
调用这个方法
genericGET(urlString: "YOUR_URL") { (persons: [PersonModel]?) in
print(persons?[0].firstname)
}