如果你的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