Alamofire 二次封裝

須導(dǎo)入 Alamofire HandyJSON

1. 基類模型

struct CoscoBaseModel: HandyJSON {
    /// 錯(cuò)誤碼
    var code: Int?
    var status: Int?
    /// 數(shù)據(jù)
    var data: Any?
    /// 提示信息
    var message: String?
    /// 成功 true  失敗 false
    var success: Bool = false
}

2. token 模型

struct AccessToken: HandyJSON, Identifiable, Codable {
    var id = UUID()
    var access_token: String?
    var token_type: String?
    var expires_in: Double?
    var storageTimestamp: TimeInterval = Date().timeIntervalSince1970
}

3. 攔截器

class CoscoRequestInterceptor: RequestInterceptor {
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        let request = sign(request: urlRequest)
        completion(.success(request))
    }
    
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        
        guard let response = request.task?.response as? HTTPURLResponse, (response.statusCode == 401)  else {
            /// 這個(gè)請求沒有因?yàn)?401 token 過期
            /// 則進(jìn)行原始請求,不重試
            return completion(.doNotRetryWithError(error))
        }
        
        // 確保只重試一次,否則會(huì)無限重試下去
        guard request.retryCount == 0 else {
            return completion(.doNotRetry)
        }

        HttpClient.getAccessToken { token in
            // 緩存token
            let json = try? JSONEncoder().encode(token)
            UserDefaults.standard.set(json, forKey: "coscoToken")
            // 1秒后進(jìn)行重試
            completion(.retryWithDelay(1))
        } errorCompletion: { msg in
            // 刷新token失敗放棄重試
            completion(.doNotRetryWithError(error))
        }
    }
    
    // header 添加 token
    private func sign(request: URLRequest) -> URLRequest {
        guard let urlString = request.url?.absoluteString else {
            return request
        }
        if urlString == Api.accessTokenUrl() {
            return request
        }
        var retRequest = request
        if let model = UserDefaults.standard.value(forKey: "coscoToken") {
            let token = try? JSONDecoder().decode(AccessToken.self, from: model as! Data)
            retRequest.setValue(token?.access_token ?? ""), forHTTPHeaderField: "Authorization")
        }
        return retRequest
    }
}

4. 調(diào)用類和方法

class HttpClient {
    class func get(_ url: String,  params: [String : Any]? = nil, encoding: ParameterEncoding = URLEncoding.default) {
        
        AF.request(url, method: .get, parameters: params, encoding: encoding, headers: nil, interceptor: CoscoRequestInterceptor(), requestModifier: nil).validate({ request, response, data in
            
            // 401 這種返回,按照請求成功處理。所以不會(huì)觸發(fā) Retrier. 此處對特定狀態(tài)碼401返回錯(cuò)誤
            let statusCode = response.statusCode
            if statusCode != 401 {
                return .success(())
            } else {
                return .failure(AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)))
            }
            
        }).responseString(encoding: .utf8) { response in
            // 用utf8 編碼,否則中文會(huì)是亂碼
            switch response.result {

            case .success(let json):
                guard let baseModel = CoscoBaseModel.deserialize(from: json) else {
                    return print("請求出錯(cuò)~")
                }

                print("數(shù)據(jù)--:", baseModel)

            case .failure(let error):
                print("錯(cuò)誤", error)
            }
        }
    }
    
    // 獲取token
    class func getAccessToken(success: @escaping (_ token : AccessToken) -> (), errorCompletion: @escaping (_ msg : String) -> ()) {
        // token 請求地址
        let url = Api.accessTokenUrl()
        let params = 此處為獲取token的參數(shù)
        let headers: HTTPHeaders = HTTPHeaders(["Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8"])
        
        // 注意:此處是用 URL 編碼參數(shù)的 POST 請求方式,不同后端開發(fā)或許不同的參數(shù)方式,具體情況具體對待
        AF.request(url, method: .post, parameters: params, encoder: URLEncodedFormParameterEncoder.default, headers: headers, interceptor: nil, requestModifier: nil).responseJSON { response in
            
            switch response.result {
            case .success(let json):
                guard let tokenModel = AccessToken.deserialize(from: json as? Dictionary) else {
                    return errorCompletion("獲取accessToken失敗")
                }
                
                success(tokenModel)
                
            case .failure(let error):
                print(error)
                errorCompletion("服務(wù)器連接出錯(cuò)啦~")
            }
        }
    }
}

注意:

用了.responseString這個(gè)閉包,必須加上參數(shù)(encoding: .utf8),否則中文會(huì)亂碼,沒有中文的數(shù)據(jù),加不加都無所謂。

PS: 讓Swift社區(qū)豐富起來吧

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

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