【问题标题】:Alamofire response object mappingAlamofire 响应对象映射
【发布时间】: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 通用对象

有人请帮我解决这个问题,现在卡在这个问题上很长一段时间了。

【问题讨论】:

    标签: ios swift swift3 alamofire objectmapper


    【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-16
        • 2019-09-24
        • 2021-12-03
        • 2011-11-25
        • 2020-08-08
        • 2019-07-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多