如何使用Codable和Almofire解析一般响应

时间:2018-11-02 07:42:21

标签: generics alamofire codable

每个API都有三个参数作为响应。

  1. 代码:指示API是成功还是失败(1或0)
  2. 消息:字符串
  3. 数据:可以是对象数组或单个对象。

我已经创建了基本模型。

struct ResponseBase<T:Codable> : Codable {

    let code : String?
    let data : [T]
    let message : String?

    enum CodingKeys: String, CodingKey {
        case code = "Code"
        case data = "Data"
        case message = "Message"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        code = try values.decodeIfPresent(String.self, forKey: .code)
        data = try values.decodeIfPresent([T].self, forKey: .data)
        message = try values.decodeIfPresent(String.self, forKey: .message)
    }
}


struct SocialWarmer : Codable {

    let createdDate : String?
    let lookUpId : String?
    let lookupKey : String?
    let lookupValue : String?
    let parentId : String?
    let statusFlag : String?
    let type : String?
    let updatedDate : String?

    enum CodingKeys: String, CodingKey {
        case createdDate = "CreatedDate"
        case lookUpId = "LookUpId"
        case lookupKey = "LookupKey"
        case lookupValue = "LookupValue"
        case parentId = "ParentId"
        case statusFlag = "StatusFlag"
        case type = "Type"
        case updatedDate = "UpdatedDate"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        createdDate = try values.decodeIfPresent(String.self, forKey: .createdDate)
        lookUpId = try values.decodeIfPresent(String.self, forKey: .lookUpId)
        lookupKey = try values.decodeIfPresent(String.self, forKey: .lookupKey)
        lookupValue = try values.decodeIfPresent(String.self, forKey: .lookupValue)
        parentId = try values.decodeIfPresent(String.self, forKey: .parentId)
        statusFlag = try values.decodeIfPresent(String.self, forKey: .statusFlag)
        type = try values.decodeIfPresent(String.self, forKey: .type)
        updatedDate = try values.decodeIfPresent(String.self, forKey: .updatedDate)
    }

}

以下是API请求的代码。

class BaseApiClient {

static let `default`  = BaseApiClient()

private init() {

}

func fetch<model:Codable>(request:APIRouter,decoder : JSONDecoder = JSONDecoder()  ,onSuccess: @escaping ([model]) -> Void) {


    if Connectivity.isReachable {
        (UIApplication.shared.delegate as! AppDelegate).addProgressView()
        Alamofire.request(request).responseJSON { (response) in
            switch response.result {
            case .success( let apiResponse) :
                DispatchQueue.main.async {
                    (UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
                }
                if let responseData = apiResponse as? [String:Any] , let status  = responseData["Code"] as? String , status == "SUCCESS" {
                    do {
                        let responseModel  = try decoder.decode(ResponseBase<model>.self, from: response.data!)
                        onSuccess(responseModel.data!)
                    }
                    catch let error as NSError {
                        print("failed reason : \(error.localizedDescription)")
                    }

                    print(model.Type.self)
                    print(model.self)




                }
                else {
                    UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: "Service not Avilabel" ,okclick: nil)
                }
            case .failure(let error) :
                UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: error.localizedDescription, okclick: nil)
            }
        }
    }
    else {
        (UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
        UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Error", message: "connnection not avilabel", okclick: nil)
    }
}

}

以下是用于调用API的代码。

BaseApiClient.default.fetch(request: APIRouter.GetSocialWarmerType) { (response: [SocialWarmer]) in
            print(response)
        }

,但是如果Data是单个对象,则此模型和API方法将不起作用。 我试图实现的目标是创建单个模型并在API方法中进行适当的更改,以解析对象数组和单个对象。

2 个答案:

答案 0 :(得分:2)

最后,我找到了一种解决方法。我创建了两个基类,一个用于对象数组,一个用于单个对象。

Name

以下是Api方法的代码。

struct ResponseBaseArray<T:Codable> : Codable {

    let code : String?
    let data : [T]?
    let message : String?

    enum CodingKeys: String, CodingKey {
        case code = "Code"
        case data = "Data"
        case message = "Message"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        code = try values.decodeIfPresent(String.self, forKey: .code)
        data = try values.decodeIfPresent([T].self, forKey: .data)
        message = try values.decodeIfPresent(String.self, forKey: .message)
    }
}


struct ResponseBaseObject<T:Codable> : Codable {

    let code : String?
    let data : T?
    let message : String?

    enum CodingKeys: String, CodingKey {
        case code = "Code"
        case data = "Data"
        case message = "Message"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        code = try values.decodeIfPresent(String.self, forKey: .code)
        data = try values.decodeIfPresent(T.self, forKey: .data)
        message = try values.decodeIfPresent(String.self, forKey: .message)
    }
}

以下是呼叫Api的代码。

class BaseApiClient {

    static let `default`  = BaseApiClient()

    private init() {

    }

    func fetch<model:Codable>(request:APIRouter , decoder: JSONDecoder = JSONDecoder() ,onSuccess: @escaping (model) -> Void) {

        if Connectivity.isReachable {
            (UIApplication.shared.delegate as! AppDelegate).addProgressView()
            Alamofire.request(request).responseJSON { (response) in
                switch response.result {
                case .success( let apiResponse) :

                    DispatchQueue.main.async {
                        (UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
                    }
                    if let responseData = apiResponse as? [String:Any] , let status  = responseData["Code"] as? String , status == "SUCCESS" {
                        do {
                            let responseModel  = try decoder.decode(model.self, from: response.data!)
                            onSuccess(responseModel)
                        }
                        catch let error as NSError {
                            print("failed reason : \(error.localizedDescription)")
                        }
                    }
                    else {
                        UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: "Service not Avilabel" ,okclick: nil)
                    }
                case .failure(let error) :
                    UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Erorr", message: error.localizedDescription, okclick: nil)
                }
            }
        }
        else {
            (UIApplication.shared.delegate as! AppDelegate).hideProgrssVoew()
            UIApplication.shared.gettopMostViewController()?.presentAlerterror(title: "Error", message: "connnection not avilabel", okclick: nil)
        }
    }

}

如果您的Api返回单个对象而不是使用ResponseBaseObject。

BaseApiClient.default.fetch(request: APIRouter.GetSocialWarmerType) { (rsult:ResponseBaseArray<[SocialWarmer]>) in
            print(rsult.data)
        }

但是仍然只有当您已经知道Api将返回单个对象或对象数组时,此解决方案才有效。

答案 1 :(得分:1)

您的响应对象应仅使用T,而不是[T]

struct ResponseObject<T: Decodable>: Decodable {
    let code: String
    let data: T?
    let message: String?

    enum CodingKeys: String, CodingKey {
        case code = "Code"
        case data = "Data"
        case message = "Message"
    }
}

然后,当您指定通用类型时,即在其中指定它是单个实例还是数组。

例如,当解码单个实例时,它是:

let responseObject = try JSONDecoder().decode(ResponseObject<Foo>.self, from: data)
let foo = responseObject.data

但是对于数组,它是:

let responseObject = try JSONDecoder().decode(ResponseObject<[Foo]>.self, from: data)
let array = responseObject.data

这是一个示例游乐场:

struct ResponseObject<T: Decodable>: Decodable {
    let code: String?
    let data: T
    let message: String?

    enum CodingKeys: String, CodingKey {
        case code = "Code"
        case data = "Data"
        case message = "Message"
    }
}

struct Foo: Codable {
    let value: Int
}

do {
    let data = """
        {
            "Code": "some code",
            "Message": "some message",
            "Data": {
                "value": 42
            }
        }
        """.data(using: .utf8)!

    let responseObject = try JSONDecoder().decode(ResponseObject<Foo>.self, from: data)
    let foo = responseObject.data
    print(foo)
} catch {
    print(error)
}

do {
    let data = """
        {
            "Code": "some code",
            "Message": "some message",
            "Data": [
                {
                    "value": 1
                }, {
                    "value": 2
                }, {
                    "value": 3
                }
            ]
        }
        """.data(using: .utf8)!

    let responseObject = try JSONDecoder().decode(ResponseObject<[Foo]>.self, from: data)
    let array = responseObject.data
    print(array)
} catch {
    print(error)
}