美文网首页swift专题
Alamofire 二次封装

Alamofire 二次封装

作者: 西风那个吹呀吹 | 来源:发表于2022-09-05 22:07 被阅读0次

    须导入 Alamofire HandyJSON

    1. 基类模型

    struct CoscoBaseModel: HandyJSON {
        /// 错误码
        var code: Int?
        var status: Int?
        /// 数据
        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 {
                /// 这个请求没有因为 401 token 过期
                /// 则进行原始请求,不重试
                return completion(.doNotRetryWithError(error))
            }
            
            // 确保只重试一次,否则会无限重试下去
            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秒后进行重试
                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. 调用类和方法

    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 这种返回,按照请求成功处理。所以不会触发 Retrier. 此处对特定状态码401返回错误
                let statusCode = response.statusCode
                if statusCode != 401 {
                    return .success(())
                } else {
                    return .failure(AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)))
                }
                
            }).responseString(encoding: .utf8) { response in
                // 用utf8 编码,否则中文会是乱码
                switch response.result {
    
                case .success(let json):
                    guard let baseModel = CoscoBaseModel.deserialize(from: json) else {
                        return print("请求出错~")
                    }
    
                    print("数据--:", baseModel)
    
                case .failure(let error):
                    print("错误", error)
                }
            }
        }
        
        // 获取token
        class func getAccessToken(success: @escaping (_ token : AccessToken) -> (), errorCompletion: @escaping (_ msg : String) -> ()) {
            // token 请求地址
            let url = Api.accessTokenUrl()
            let params = 此处为获取token的参数
            let headers: HTTPHeaders = HTTPHeaders(["Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8"])
            
            // 注意:此处是用 URL 编码参数的 POST 请求方式,不同后端开发或许不同的参数方式,具体情况具体对待
            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("服务器连接出错啦~")
                }
            }
        }
    }
    

    注意:

    用了.responseString这个闭包,必须加上参数(encoding: .utf8),否则中文会乱码,没有中文的数据,加不加都无所谓。

    PS: 让Swift社区丰富起来吧

    相关文章

      网友评论

        本文标题:Alamofire 二次封装

        本文链接:https://www.haomeiwen.com/subject/yppbnrtx.html