使用 Swift 4 Codable 的通用响应对象序列化
如果您不想使用像 ObjectMapper 这样的其他依赖项,您可以执行以下方式,但您可能需要进行一些更改。
Following 是我们使用 Alamofire 使用泛型反序列化 JSON 数据的典型模型。 Alamofire 上有很多例子和优秀的documentation。
struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
let username: String
let name: String
var description: String {
return "User: { username: \(username), name: \(name) }"
}
init?(response: HTTPURLResponse, representation: Any) {
guard
let username = response.url?.lastPathComponent,
let representation = representation as? [String: Any],
let name = representation["name"] as? String
else { return nil }
self.username = username
self.name = name
}
}
使用 Swift 4 中引入的 Codable 协议
typealias Codable = Decodable & Encodable
朝着这个方向迈出的第一步是添加帮助函数
将在反序列化 JSON 数据和处理
错误。使用 Swift 扩展,我们添加了解码传入的函数
JSON 到我们将在之后编写的模型结构/类中。
let decoder = JSONDecoder()
let responseObject = try? decoder.decode(T.self, from: jsonData)
The decoder (1) is an object that decodes instances of a data type from JSON objects.
辅助函数
extension DataRequest{
/// @Returns - DataRequest
/// completionHandler handles JSON Object T
@discardableResult func responseObject<T: Decodable> (
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!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
// (1)- Json Decoder. Decodes the data object into expected type T
// throws error when failes
let decoder = JSONDecoder()
guard let responseObject = try? decoder.decode(T.self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON object could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseObject)
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
/// @Returns - DataRequest
/// completionHandler handles JSON Array [T]
@discardableResult func responseCollection<T: Decodable>(
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!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
let decoder = JSONDecoder()
guard let responseArray = try? decoder.decode([T].self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON array could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseArray)
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
第二,我之前提到过“使用 Swift 4 Codable”,但如果我们都
想要的是从服务器解码JSON,我们只需要一个模型
符合可解码协议的结构/类。 (如果你有
您要上传的相同结构可以使用 Codable 来处理
解码和编码)所以,现在我们的用户模型结构现在看起来像
这个。
struct User: Decodable, CustomStringConvertible {
let username: String
let name: String
/// This is the key part
/// If parameters and variable name differ
/// you can specify custom key for mapping "eg. 'user_name'"
enum CodingKeys: String, CodingKey {
case username = "user_name"
case name
}
var description: String {
return "User: { username: \(username), name: \(name) }"
}
}
最后,我们对 API 的函数调用如下所示。
Alamofire.request(Router.readUser("mattt"))).responseObject{ (response: DataResponse<User>) in
// Process userResponse, of type DataResponse<User>:
if let user = response.value {
print("User: { username: \(user.username), name: \(user.name) }")
}
}
对于更复杂(嵌套)的 JSON,逻辑保持不变,您需要在模型结构/类中进行的唯一修改是所有结构/类必须符合 Decodable 协议,而 Swift 会处理其他所有事情。