Swift網(wǎng)絡(luò)庫最著名的就是Alamofire,但是在實際項目中,想要盡可能簡單的發(fā)起網(wǎng)絡(luò)請求,必須對Alamofire進(jìn)行一層封裝。依賴Alamofire的封裝庫也有很多優(yōu)秀的,比如Moya。但是經(jīng)過項目的使用,我發(fā)現(xiàn)Moya用起來并不簡單,它有以下問題:
- 硬編碼較多
Moya使用時要在指定的方法中添加請求方法,請求參數(shù),路徑。 - 封裝復(fù)雜
Alamofire已經(jīng)是一個功能繁多的網(wǎng)絡(luò)庫了,Moya的二次封裝,既要保證功能完整,又要使用面向協(xié)議,導(dǎo)致了過多的抽象,用戶在使用時,大都需要三次業(yè)務(wù)的封裝,雖然也面向協(xié)議了,但卻復(fù)雜了。
因此對于眾多中小項目而言,筆者覺得Moya并不是一個合適的選項。合適的網(wǎng)絡(luò)庫,應(yīng)該是對基礎(chǔ)庫(Alamofire)的精簡,保證既簡單易用,又有靈活的擴展性。
那發(fā)起的請求,要簡化到什么程度?
只需指定參數(shù)和路徑,就可以發(fā)起請求,最好使用類方法,來直接發(fā)起請求。
而簡化的越徹底,其他地方要做的就越多,因此,通常情況下,也要設(shè)計出以下功能:
- 統(tǒng)一設(shè)置公共參數(shù)
- 統(tǒng)一設(shè)置請求頭
- 統(tǒng)一的設(shè)置加載指示器
- 統(tǒng)一解析數(shù)據(jù)
- 等等
我根據(jù)以上要求,花了兩天的空閑時間,寫了一個輕量級的網(wǎng)絡(luò)庫LightNetwork,拋磚引玉。
該庫使用面向?qū)ο蟮姆庋b邏輯,下面是它的介紹。
發(fā)起請求
比如發(fā)起一個修改用戶名的請求,外部暴露參數(shù)userName即可,方法內(nèi)部指定path和parameter。調(diào)用時直接使用類方法調(diào)用。
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)")
}
如果覺得統(tǒng)一管理URL更好,也可以使用靜態(tài)字符串。
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)
}
全局配置
設(shè)置BaseURL:
var config = LNNetworkConfiguration(baseURL: URL(string: "https://example.com/"))
config.networkInterceptor = ExampleLightInterceptor.init()
LNNetworkManager.default.configuration = config
其中config.networkInterceptor是對所有網(wǎng)絡(luò)請求的攔截器,可在攔截器中統(tǒng)一的添加參數(shù),請求頭。
配置全局?jǐn)r截器:
class ExampleLightInterceptor: LNNetworkInterceptor {
/// 添加公共參數(shù)
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)")
}
//請求結(jié)束??膳渲秒[藏加載指示器
func interceptor(end request: LNBaseRequest) {
print("Request end: \(request.url)")
}
}
配置統(tǒng)一的響應(yīng)處理
class ExampleBaseRequest: LNRequest {
//自定義error code,并使用LNRequestFailure返回
var errorCode : Int = 0
var responseData : Any?
// 自定義統(tǒng)一處理響應(yīng)結(jié)果
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)
}
}
}
經(jīng)過上述的配置,基本上滿足的日常網(wǎng)絡(luò)請求的需要,同時支持很方便的下載,上傳,進(jìn)度監(jiān)控,設(shè)置緩存邏輯,超時時長等。詳細(xì)的配置可以參考示例代碼.
兼容性
LightNetwork中的請求方法,本身就是調(diào)用Alamofire的請求方法,LightNetwork所做的只是Alamofire多參數(shù)的方法進(jìn)行類化。即將Alamofire請求方法的參數(shù),寫成LightNetwork LNBaseRequest類的屬性,這樣即方便了統(tǒng)一配置,也便于Alamofire方法的簡化。同時,返回參數(shù)保持和Alamofire一致,保留了Alamofire方法的鏈?zhǔn)秸{(diào)用。
//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?
}
以上~