封裝一個超級易用的Swift網(wǎng)絡(luò)請求庫

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? 
}

以上~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容