时光地下铁

走走停停,看看风景

0%

封装一个超级易用的Swift网络请求库

Swift网络库最著名的就是Alamofire,但是在实际项目中,想要尽可能简单的发起网络请求,必须对Alamofire进行一层封装。依赖Alamofire的封装库也有很多优秀的,比如Moya。但是经过项目的使用,我发现Moya用起来并不简单,它有以下问题:

  • 硬编码较多
    Moya使用时要在指定的方法中添加请求方法,请求参数,路径。
  • 封装复杂
    Alamofire已经是一个功能繁多的网络库了,Moya的二次封装,既要保证功能完整,又要使用面向协议,导致了过多的抽象,用户在使用时,大都需要三次业务的封装,虽然也面向协议了,但却复杂了。

因此对于众多中小项目而言,笔者觉得Moya并不是一个合适的选项。合适的网络库,应该是对基础库(Alamofire)的精简,保证既简单易用,又有灵活的扩展性。

那发起的请求,要简化到什么程度?
只需指定参数路径,就可以发起请求,最好使用类方法,来直接发起请求。
而简化的越彻底,其他地方要做的就越多,因此,通常情况下,也要设计出以下功能:

  • 统一设置公共参数
  • 统一设置请求头
  • 统一的设置加载指示器
  • 统一解析数据
  • 等等

我根据以上要求,花了两天的空闲时间,写了一个轻量级的网络库LightNetwork,抛砖引玉。
该库使用面向对象的封装逻辑,下面是它的介绍。

发起请求

比如发起一个修改用户名的请求,外部暴露参数userName即可,方法内部指定path和parameter。调用时直接使用类方法调用。

1
2
3
4
5
6
7
8
9
10
11
class ExampleRequest : ExampleBaseRequest {
class func modify(userName:String,success: @escaping LNRequestSuccess, failure:@escaping LNRequestFailure) {
self.post(path: "/modify", parameters: ["userName":userName], success: success, failure: failure)
}
}
//use
ExampleRequest.modify(userName: "Light") { request, responseData in
print("Success:\(responseData)")
} failure: { request, errorDescription in
print("Failure:\(errorDescription)")
}

如果觉得统一管理URL更好,也可以使用静态字符串。

1
2
3
4
5
6
7
8
9
struct URLPath {
static let query : String = "/query"
static let goods : String = "/goods"
static let modify : String = "/modify"
}

class func modify(userName:String,success: @escaping LNRequestSuccess, failure:@escaping LNRequestFailure) {
self.post(path: URLPath.modify, parameters: ["userName":userName], success: success, failure: failure)
}

全局配置

设置BaseURL:

1
2
3
var config = LNNetworkConfiguration(baseURL: URL(string: "https://example.com/"))
config.networkInterceptor = ExampleLightInterceptor.init()
LNNetworkManager.default.configuration = config

其中config.networkInterceptor是对所有网络请求的拦截器,可在拦截器中统一的添加参数,请求头。
配置全局拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ExampleLightInterceptor: LNNetworkInterceptor {
/// 添加公共参数
func interceptor(_ request: LNBaseRequest, parameter: Parameters?) -> Parameters? {
var para : Dictionary<String, Any> = parameter ?? Dictionary<String, Any>.init()
para["public"] = "test"
return para
}
/// 添加公共请求头
func interceptor(_ request: LNBaseRequest, headerFields: Alamofire.HTTPHeaders?) -> Alamofire.HTTPHeaders? {
var header = HTTPHeaders();
headerFields?.dictionary.forEach { header.add(name: $0.key, value: $0.value) }
header.add(name: "publicHeader", value: "test")
return header
}
//请求开始。可配置显示加载指示器
func interceptor(start request: LNBaseRequest) {
print("Request start: \(request.url)")
}
//请求结束。可配置隐藏加载指示器
func interceptor(end request: LNBaseRequest) {
print("Request end: \(request.url)")
}
}

配置统一的响应处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ExampleBaseRequest: LNRequest {
//自定义error code,并使用LNRequestFailure返回
var errorCode : Int = 0
var responseData : Any?
// 自定义统一处理响应结果
override func process(response: AFDataResponse<Data?>, success: LNRequestSuccess?, failure: LNRequestFailure? = nil) {
switch response.result {
case let .success(data):
guard let sourceData = data else { return }
do {
let jsonData = try JSONSerialization.jsonObject(with: sourceData, options: .allowFragments)

let code : Int = (jsonData as! [String : Any])["code"] as! Int
if code == 200 {
success?(self, jsonData)
}else {
failure?(self, ExampleServerError.invalidParameter)
}

} catch {
let error = AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))
failure?(self, error)
}
case let .failure(error):
failure?(self, error)
}
}
}

经过上述的配置,基本上满足的日常网络请求的需要,同时支持很方便的下载,上传,进度监控,设置缓存逻辑,超时时长等。详细的配置可以参考示例代码.

兼容性

LightNetwork中的请求方法,本身就是调用Alamofire的请求方法,LightNetwork所做的只是Alamofire多参数的方法进行类化。即将Alamofire请求方法的参数,写成LightNetwork LNBaseRequest类的属性,这样即方便了统一配置,也便于Alamofire方法的简化。同时,返回参数保持和Alamofire一致,保留了Alamofire方法的链式调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Alamofire
open func request(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
requestModifier: RequestModifier? = nil)
-> DataRequest
//LightNetwork
open class LNBaseRequest: NSObject {
open var path: String = ""
open var method: HTTPMethod = .get
open var parameters: Parameters?
open var encoding: ParameterEncoding = URLEncoding.default
open var headers: HTTPHeaders?
open var interceptor: RequestInterceptor?
open var requestModifier: Alamofire.Session.RequestModifier?
open var cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy
open var timeoutInterval: TimeInterval = 60

open class func request(path: String,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
success: LNRequestSuccess?,
failure: LNRequestFailure?)
-> DataRequest?
}

以上~