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) } } 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 { 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 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 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 ? }
以上~