irpas技术客

Swift -《从0到1 - 5》:封装网络请求工具类(Alamofire + Moya + SwiftyJSON)和链式封装_西半球_swift 网络请求封装

未知 4319

在OC开发中网络请求通常都使用AFNetworking,在Swift虽然也可以使用,但是推荐使用Swift语法实现的网络请求库Alamofire。 通常大家会对请求库进行一次或多次封装,方便维护,我也不另外。刚开始参考了很多文章,最终决定使用Alamofire + Moya + SwiftyJSON 实现网络请求工具类和API管理类 本篇末尾介绍另一种通过链式封装Alamofire的实现方式

GitHub Demo 地址

Alamofire是一个使用Swift开发的网络请求库,其开发团队是AFNetworking的原团队。它语法简洁,采用链式编程的思想,使用起来是相当的舒服。本质是基于NSURLSession进行封装。 Moya是对Alamofire的再次封装。 SwiftyJSON是数据解析

github地址:

Alamofire Moya SwiftyJSON

pods引用: pod 'Alamofire', '4.9.1' pod 'Moya', '13.0.1' pod 'SwiftyJSON', '5.0.1'

首先需要创建3个Swift文件: 一个是网络请求工具类:JhHttpTool.swift 一个Moya配置文件:MoyaConfig.swift 一个API管理文件:APIManager.swift

接下来直接上代码

JhHttpTool // // JhHttpTool.swift // JhSwiftDemo // // Created by Jh on 2021/12/28. // 网络请求工具类:Alamofire + Moya + SwiftyJSON import Foundation import Moya import SwiftyJSON public class JhHttpTool { /// 使用Moya的请求封装 /// /// - Parameters: /// - target: 请求API,TargetType里的枚举值 /// - success: 成功的回调 /// - error: 连接服务器成功但是数据获取失败 /// - failure: 连接服务器失败 public class func request<T: TargetType>(_ target: T, success: @escaping((Any) -> Void), failure: ((Int?, String) ->Void)?) { let provider = MoyaProvider<T>(plugins: [ RequestHandlingPlugin(), // networkLoggerPlugin ]) provider.request(target) { result in switch result { case let .success(response): // let json = try? response.mapString() // let responseObject = try? response.mapJSON() // JhLog( responseObject ?? "" ); do { // *********** 这里可以统一处理错误码,弹出提示信息 *********** let resObject = try? response.mapJSON() let responseObject = JSON(resObject ?? "") let code = responseObject["code"].intValue let msg = String(describing: responseObject["msg"]) switch (code) { case 200 : // 数据返回正确 success(responseObject) case 401: // 请重新登录 failure!(code,msg) alertLogin(msg) default: // 其他错误 failureHandle(failure: failure, stateCode: code, message: msg) } } case let .failure(error): let statusCode = error.response?.statusCode ?? 1000 let message = "请求出错,错误码:" + String(statusCode) JhAllLog(message) failureHandle(failure: failure, stateCode: statusCode, message: error.errorDescription ?? message) } } // 错误处理 - 弹出错误信息 func failureHandle(failure: ((Int?, String) ->Void)? , stateCode: Int?, message: String) { Alert.show(type: .error, text: message) failure?(stateCode ,message) } // 登录弹窗 - 弹出是否需要登录的窗口 func alertLogin(_ title: String?) { // TODO: 跳转到登录页的操作: } } // MARK: - 打印日志 // static let networkLoggerPlugin = NetworkLoggerPlugin(verbose: true, cURL: true, requestDataFormatter: { data -> String in // return String(data: data, encoding: .utf8) ?? "" // }) { data -> (Data) in // do { // let dataAsJSON = try JSONSerialization.jsonObject(with: data) // let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted) // return prettyData // } catch { // return data // } // } } MoyaConfig // // JhHttpRequest.swift // JhSwiftDemo // // Created by Jh on 2022/2/10. // Moya 配置文件 import Foundation import Moya // MARK: - 1、2需要根据项目进行更改 /** 1、配置TargetType协议可以一次性处理的参数 - Todo: 根据自己的需要更改,不能统一处理的移除下面的代码,并在APIManager中实现 **/ public extension TargetType { // // 放到APIManager中了 // var baseURL: URL { // return URL(string: "http://xxxxx")! // } var headers: [String : String]? { return nil } var sampleData: Data { return "{}".data(using: String.Encoding.utf8)! } } /** 2、公共参数 - Todo: 配置公共参数,例如所有接口都需要传token,version,time等,就可以在这里统一处理 - Note: 接口传参时可以覆盖公共参数。下面的代码只需要更改 【private var commonParams: [String: Any]?】 **/ extension URLRequest { //TODO:处理公共参数 private var commonParams: [String: Any]? { //所有接口的公共参数添加在这里: let header = [ "Content-Type": "application/x-www-form-urlencoded", "systemType": "iOS", "version": "1.0.0", "token": getToken(), ] return header // 如果不需要传空 // return nil } private func getToken() -> String { return "1" } } //下面的代码不更改 class RequestHandlingPlugin: PluginType { public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest { var mutateableRequest = request return mutateableRequest.appendCommonParams(); } } //下面的代码不更改 extension URLRequest { mutating func appendCommonParams() -> URLRequest { let request = try? encoded(parameters: commonParams, parameterEncoding: URLEncoding(destination: .queryString)) assert(request != nil, "append common params failed, please check common params value") return request! } func encoded(parameters: [String: Any]?, parameterEncoding: ParameterEncoding) throws -> URLRequest { do { return try parameterEncoding.encode(self, with: parameters) } catch { throw MoyaError.parameterEncoding(error) } } } APIManager // // APIManager.swift // JhSwiftDemo // // Created by Jh on 2021/12/28. // 接口管理 import Foundation import Moya /// 基础域名 let kBaseURL = "https://www.fastmock.site/mock/1010b262a743f0b06c565c7a31ee9739/root" enum API { case login(params:Dictionary<String,Any>) // 获取分页数据 case getPageList(_ page:Int) // 获取分组分页数据 case getGroupPageList(page:Int) // 获取联系人数据 case getContact // 获取微信运行排行榜 case getWxMotionTops // 获取固定数据 case getSimpleArrDic ///其他接口... case other1(p1: String, p2: Int, p3: String, p4: String) case other2 } // MARK: - 补全【MoyaConfig 3:配置TargetType协议可以一次性处理的参数】中没有处理的参数 extension API: TargetType { //0. 基础域名,整个项目只用一个,可以写在MoyaConfig中 var baseURL: URL { switch self { case .login: return URL(string:kBaseURL)! default: return URL(string:kBaseURL)! } } //1. 每个接口的相对路径 //请求时的绝对路径是 baseURL + path var path: String { switch self { case .login: return "/login" case .getPageList: return "/mock/pages" case .getGroupPageList: return "/mock/groupPages" case .getContact: return "/mock/contacts" case .getWxMotionTops: return "/mock/wxMotionTops" case .getSimpleArrDic: return "/getSimpleArrDic" case let .other1(p1, p2, _, _): return "/list?id=\(p1)&page=\(p2)" case .other2: return "" } } //2. 每个接口要使用的请求方式 var method: Moya.Method { switch self { case .getPageList, .getGroupPageList, .other1, .other2: return .get case .getContact, .getWxMotionTops, .getSimpleArrDic, .login: return .post } } //3. Task是一个枚举值,根据后台需要的数据,选择不同的http task。 var task: Task { var params: [String: Any] = [:] switch self { case .login: return .requestPlain case let .getPageList(page): params["page"] = page params["limit"] = 15 params["maxCount"] = 100 case let .other1(_, _, p3, p4): params["p3"] = p3 params["p4"] = p4 default: //不需要传参数的接口走这里 return .requestPlain } return .requestParameters(parameters: params, encoding: URLEncoding.default) } } 使用 // Alamofire + Moya + SwiftyJSON JhHttpTool.request(API.getPageList(1)) {[weak self] json in self?.mTextView.text = String(describing: JSON(json)) JhAllLog(JSON(json)) } failure: {code, msg in JhLog("code : \(code!)") JhLog("message : \(msg)") } 链式封装Alamofire

链式封装,API可以直接通过一个文件进行管理,里面只放url路径

// // JhRequest.swift // JhSwiftDemo // // Created by Jh on 2021/12/28. // 链式网络请求工具类:Alamofire + SwiftyJSON import Foundation import Alamofire import SwiftyJSON enum HttpRequestType { case get case post } public let JhRequest = NetworkKit.shared // Networkkit属性设置 public class NetworkKit { public static let shared = NetworkKit() typealias SuccessHandlerType = ((JSON) -> Void) typealias FailureHandlerType = ((Int?, String) ->Void) private var requestType: HttpRequestType = .post//请求类型 private var url: String? // URL private var params: [String: Any]? // 参数 private var success: SuccessHandlerType? // 成功的回调 private var failure: FailureHandlerType? // 失败的回调 private var httpRequest: Request? } // NetworkKit属性的设置 extension NetworkKit{ /// 设置url func url(_ url: String?) -> Self { self.url = url return self } /// 设置post/get 默认post func requestType(_ type:HttpRequestType) -> Self { self.requestType = type return self } /// 设置参数 func params(_ params: [String: Any]?) -> Self { self.params = params return self } /// 成功的回调 func success(_ handler: @escaping SuccessHandlerType) -> Self { self.success = handler return self } ///失败的回调 func failure(handler: @escaping FailureHandlerType) -> Self { self.failure = handler return self } } // NetworkKit请求相关 extension NetworkKit{ /// 发起请求 设置好相关参数后再调用 func request() -> Void { var dataRequest: DataRequest? // alamofire请求后的返回值 // 发起请求 if let URLString = url { ProgressHUD.show() let method = requestType == .get ? HTTPMethod.get : HTTPMethod.post dataRequest = Alamofire.request(URLString, method: method, parameters: params) httpRequest = dataRequest } dataRequest?.responseJSON { (response) in ProgressHUD.hide() switch response.result { case let .success(response): do { // *********** 这里可以统一处理错误码,弹出提示信息 *********** let responseObject = JSON(response) let code = responseObject["code"].intValue let msg = String(describing: responseObject["msg"]) switch (code) { case 200 : // 数据返回正确 self.success?(responseObject) case 401: // 请重新登录 self.failure?(code,msg) alertLogin(msg) default: // 其他错误 failureHandle(failure: self.failure, stateCode: code, message: msg) } } case let .failure(error): failureHandle(failure: self.failure, stateCode: nil, message: error.localizedDescription) } } // 错误处理 - 弹出错误信息 func failureHandle(failure: FailureHandlerType? , stateCode: Int?, message: String) { Alert.show(type: .error, text: message) failure?(stateCode ,message) } // 登录弹窗 - 弹出是否需要登录的窗口 func alertLogin(_ title: String?) { // TODO: 跳转到登录页的操作: } } // 取消请求 func cancel() { httpRequest?.cancel() } } 使用 // 链式网络请求:Alamofire + SwiftyJSON let url = kBaseURL + "/getSimpleArrDic" JhRequest.url(url).params([:]).requestType(.post).success { res in JhLog(" ========链式网络请求======== ") JhAllLog(res) JhAllLog(res["code"]) }.failure { code, msg in JhLog("code : \(code!)") JhLog("message : \(msg)") }.request()


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Swift #网络请求封装 #moya #SwiftyJSON