Alamofire二次封装

1,418 阅读4分钟

如果你的iOS工程是用swift语言进行开发的,那大概率会使用到网络请求框架Alamofire。Alamofire对URLSession进行了封装,很多时候我们不需要做额外的处理,直接简单的调用Alamofire提供的接口就能进行网络请求。但是在我们自己的业务开发中,会对Alamofire进行封装,因为我们需要把某些统一处理的东西封装起来,比如header和后端返回的数据格式。

如某个APP后端返回的数据格式统一为:

{
    "code": 0, //0:成功 1:失败
    "message": "" //错误提示消息
    "data": {}  //具体数据
}

我们在封装的Alamofire的网络请求中统一处理解析,这样每次用的时候就只需要判断是否成功。

话不多说,上代码:

先来看下封装好的接口定义:


typealias CYARequestComplate<V> = (_ response: V) -> Void

/** 网络请求接口
     V:BaseResponse或其子类,可自定义子类封装数据解析的成功和失败
     T:请求完之后返回的数据类型,可以是任意类型
     config: 请求配置
     toTransform:需要封装的model block
     compete:请求完成回调,回调返回遵循IBaseResponse协议的对象
**/
func request<T, V: BaseResponse<T>>(_ config: INetConfiguration,
                                  toTansform: (([String: Any]?) -> T?)?,
                                  complation: CYARequestComplate<V>?)
接口中的config

config是一个遵循了INetConfiguration接口的类实例:

protocol INetConfiguration {

    var method: HTTPMethod {get set}  //请求方式

    var url: String {get set}      //请求url

    var requestParams: [String: Any]? {get set}//请求参数

    var publicHeader: HTTPHeaders {get set//私有请求头

    var timeoutInterval: TimeInterval {get set//超时时间

}

使用方可以直接在自己的config类中定义好默认的publicHeader,比如我使用的时候定义了一个通用的config类,当然可以使用提供好的DefaultConfiguration:

class NetConfiguration: INetConfiguration {    
    var url: String
    var requestParams: [String : Any]?
    var publicHeader: HTTPHeaders = ["system": "ios"]
    var timeoutInterval: TimeInterval = 300
    var method: HTTPMethod

    init(url: String,
         requestParams: [String : Any]? = nil,
         method: HTTPMethod = .get,
         appendHeader: [String : String]? = nil)  //appendHeader有的接口需要加入其他额外的header
    {
        self.url = url
        self.requestParams = requestParams
        self.method = method
        if let header = appendHeader {
            for (key, value) in header  {
                publicHeader[key] = value
            }
        }
    }
}
接口中结果回调中的response

response封装了一系列请求结果

class BaseResponse<T> {
    var data: T?  //返回的数据类型,任意类型
    var error: Error?   //返回失败后的error对象
    var message: String?   //返回失败的message
    var result: ResponseResult  //成功/失败/请求超时枚举
    var httpStatus: Int?    //http状态码

    required init() {
        result = .Failure
    }

    //子类重写自己实现解析数据
    class func fromData(_ data: Data?) -> BaseResponse<OriginResponseDataType> {
        let model = BaseResponse<OriginResponseDataType>()
        model.data = parseData(data)
        model.result = .Success
        return model
    }

    //后端返回的数据类型为string、dict、array
    class func parseData(_ data: Data?) -> OriginResponseDataType? {
        if let result = String.fromData(data) {
            return result
        }

        if let result = Dictionary.fromData(data) {
            return result
        }
        
        if let result = Array.fromData(data) {
            return result
        }
        return nil
    }
}

BaseResponse基类,子类可重新实现自己的解析,比如:

class NetResponse<T>: BaseResponse<T> {
    var code: Int?
    
    override class func fromData(_ data: Data?) -> BaseResponse<OriginResponseDataType> {
        let model = NetResponse<OriginResponseDataType>()
        let json = Dictionary.fromData(data) ?? [:]   //data返回的是json类型,BaseResponse中定义了dict、array和string类型
        model.message = json["message"] as? String
        model.code = json["code"] as? Int
        model.result = model.code == 0 ? .Success : .Failure  //code = 0时为成功
        model.data = json["data"] as? [String: Any//需要用到的data,如果
        return model
    }
}

如果工程中有不同数据类型的解析,就可以添加不同的BaseResponse子类去处理。

使用方式

1、返回结果类型是dict(只支持String、dict、array,其他类型的返回在toTransform中处理)


NetWork.manager.request(config) { (response: NetResponse<[String: Any]>) in
    if response.result == .success {
        print("response.data 是一个字典 = \(response.data)")
    }
}

2、返回结果是在toTransform中进行封装的

    NetWork.manager.request(config) { json in
         let model = MyModel(data: json)
         model.id = "1"
         return model
    } complation: { (response: NetResponse) in
         if response.result == .success {
            print("response.data 是一个MyModel类型 = \(response.data)")
         }
    }

3、返回类型遵循了其他解析库(如HandyJSON)

由于返回的数据类型用的是泛型,所以如果你工程中用到了一些解析的三方库,就可以自己添加NetWork的分类去统一处理,我这里用HandyJSON举个例子:

extension NetWork {
    func request<T, V: BaseResponse<T>>(_ config: INetConfiguration, complation: CYARequestComplate<V>?) where T : HandyJSON {
        request(config, toTansform: { json in
            return T.deserialize(from: json)
        }, complation: complation)
    }
}

//调用的时候只需要
fun test() {
    NetWork.manager.request(config) { (response: NetResponse<NetWorkModel>) in
        if response.result == .success {
            print("response.data 是一个遵循了HandyJSON协议的类 = \(response.data)")
         }
    }
}
总结:

1、如果工程中server返回的数据类型比较固定,可以自己实现一个或多个BaseResponse的子类,其内部重新fromData方法进行解析去判断请求是否成功,或者做其他操作。

2、如果请求的时候用统一的header需要传给后端,也可以写一个自己的config类,去统一处理header

3、可以很方便的兼容不同的三方json转模型的工具类,如HandyJSON

4、回调的response需要显示的标注好具体类型:如NetResponse<[String: Any]>、NetResponse,如果是toTransform的方式返回了具体的data类型了,NetResponse后面不需要再跟<>

demo:Github