Alamofire 响应对象映射
Posted
技术标签:
【中文标题】Alamofire 响应对象映射【英文标题】:Alamofire response object mapping 【发布时间】:2017-11-03 04:23:40 【问题描述】:我是 swift 3 编程新手,我正在使用 Alamofire 进行 api 调用并避免繁琐的 json 配对,我正在使用 AlamofireObjectMapper 库。
我有一个ApiController
,它有一个调用api的函数,下面是它的代码:
public static func makePostRequest<T: Mappable>(url: String, params: Parameters, networkProtocol: NetworkProtocol, responseClass: T)
let headers = getHeaders()
networkProtocol.showProgress()
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.validate()
.responseData response in
let json = response.result.value
var jsonString = String(data: json!, encoding: String.Encoding.utf8)
let responseObject = responseClass(JSONString: jsonString!)
switch(response.result)
case .success(_):
networkProtocol.hideProgress()
networkProtocol.onResponse(response: response)
break
case .failure(_):
networkProtocol.hideProgress()
networkProtocol.onErrorResponse(response: response)
break
我从服务器获取的 Json 响应模板是:
"some_int": 10,
"some_array":[...]
下面是我的模型类:
import ObjectMapper
class BaseResponse: Mappable
var some_int: Int?
var some_array: [Array_of_objects]?
required init?(map: Map)
some_int <- map["some_int"]
some_array <- map["some_array"]
func mapping(map: Map)
下面是类调用 api 的函数:
public static func callSomeApi(params: Parameters, networkProtocol: NetworkProtocol)
ApiHelper.makePostRequest(url: AppConstants.URLs.API_NAME, params: params, networkProtocol: networkProtocol, responseClass: BaseResponse)
现在错误在下面一行
let responseObject = responseClass(JSONString: jsonString!)
我无法理解如何将 jsonString
转换为我从视图控制器接受的 responseClass
通用对象
有人请帮我解决这个问题,现在卡在这个问题上很长一段时间了。
【问题讨论】:
【参考方案1】:您可以使用 SwiftyJSON:https://cocoapods.org/pods/SwiftyJSON
这里有一些示例代码可以帮助你:
Alamofire.request(endpointURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: nil).validate().responseJSON()
(response) in
if response.result.isFailure
print("ERROR! Reverse geocoding failed!")
else if let value = response.result.value
var country: String? = nil
var county: String? = nil
var city: String? = nil
var town: String? = nil
var village: String? = nil
print("data: \(value)")
let json = JSON(value)
print("json: \(json)")
country = json["address"]["country"].string
county = json["address"]["county"].string
city = json["address"]["city"].string
town = json["address"]["town"].string
village = json["address"]["village"].string
else
print("Cannot get response result value!")
请让您知道代码已被简化(很多行已被删除)并从我的实际项目中粘贴到此处,因此此代码未经测试,可能包含拼写错误或类似内容,但逻辑是可见
【讨论】:
感谢您的建议,我的问题是我想将响应保存在 SwiftyJson 无法帮助的 BaseResponse 模型类中。 好的,很抱歉给我带来了误解!【参考方案2】:对于对象映射,您需要遵循 AlamofireObjectMapper 。
//Declare this before ViewLoad
var BaseResponse: Array<BaseResponse>?
// After you receive response from API lets say "data"
if let jsonData = data as? String
self.BaseResponse = Mapper< BaseResponse >().mapArray(JSONString: jsonData)
【讨论】:
响应不是一个数组,它只是一个 json 对象。 "some_int": 10 "some_array":[...] 这是我从服务器获得的 Json 响应模板【参考方案3】:使用 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 会处理其他所有事情。
【讨论】:
【参考方案4】:你可以使用 AlamofireMapper:
带json:
"page":1,
"per_page":3,
"total":12,
"total_pages":4,
"data":[
"id":1,
"first_name":"George",
"last_name":"Bluth",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
,
"id":2,
"first_name":"Janet",
"last_name":"Weaver",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
,
"id":3,
"first_name":"Emma",
"last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
]
Swift 类:
class UserResponse: Decodable
var page: Int!
var per_page: Int!
var total: Int!
var total_pages: Int!
var data: [User]?
class User: Decodable
var id: Double!
var first_name: String!
var last_name: String!
var avatar: String!
使用 alamofire 请求
let url1 = "https://raw.githubusercontent.com/sua8051/AlamofireMapper/master/user1.json"
Alamofire.request(url1, method: .get
, parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject (response: DataResponse<UserResponse>) in
switch response.result
case let .success(data):
dump(data)
case let .failure(error):
dump(error)
链接:https://github.com/sua8051/AlamofireMapper
【讨论】:
以上是关于Alamofire 响应对象映射的主要内容,如果未能解决你的问题,请参考以下文章
尝试将 AlamoFire JSON 响应映射到类并接收“致命错误:在展开可选时意外发现 nil”