javascript
Swift -《从0到1 - 5》:封装网络请求工具类(Alamofire + Moya + SwiftyJSON)和链式封装
在OC開發(fā)中網(wǎng)絡請求通常都使用AFNetworking,在Swift雖然也可以使用,但是推薦使用Swift語法實現(xiàn)的網(wǎng)絡請求庫Alamofire。
通常大家會對請求庫進行一次或多次封裝,方便維護,我也不另外。剛開始參考了很多文章,最終決定使用Alamofire + Moya + SwiftyJSON 實現(xiàn)網(wǎng)絡請求工具類和API管理類
本篇末尾介紹另一種通過鏈式封裝Alamofire的實現(xiàn)方式
GitHub Demo 地址
Alamofire是一個使用Swift開發(fā)的網(wǎng)絡請求庫,其開發(fā)團隊是AFNetworking的原團隊。它語法簡潔,采用鏈式編程的思想,使用起來是相當?shù)氖娣1举|(zhì)是基于NSURLSession進行封裝。
Moya是對Alamofire的再次封裝。
SwiftyJSON是數(shù)據(jù)解析
github地址:
Alamofire
Moya
SwiftyJSON
pods引用:
pod 'Alamofire', '4.9.1' pod 'Moya', '13.0.1' pod 'SwiftyJSON', '5.0.1'首先需要創(chuàng)建3個Swift文件:
一個是網(wǎng)絡請求工具類:JhHttpTool.swift
一個Moya配置文件:MoyaConfig.swift
一個API管理文件:APIManager.swift
接下來直接上代碼
JhHttpTool
// // JhHttpTool.swift // JhSwiftDemo // // Created by Jh on 2021/12/28. // 網(wǎng)絡請求工具類:Alamofire + Moya + SwiftyJSON import Foundation import Moya import SwiftyJSONpublic class JhHttpTool {/// 使用Moya的請求封裝////// - Parameters:/// - target: 請求API,TargetType里的枚舉值/// - success: 成功的回調(diào)/// - error: 連接服務器成功但是數(shù)據(jù)獲取失敗/// - 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 inswitch result {case let .success(response):// let json = try? response.mapString()// let responseObject = try? response.mapJSON()// JhLog( responseObject ?? "" );do {// *********** 這里可以統(tǒng)一處理錯誤碼,彈出提示信息 ***********let resObject = try? response.mapJSON()let responseObject = JSON(resObject ?? "")let code = responseObject["code"].intValuelet msg = String(describing: responseObject["msg"])switch (code) {case 200 :// 數(shù)據(jù)返回正確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 ?? 1000let 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: 跳轉(zhuǎn)到登錄頁的操作:}}// 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需要根據(jù)項目進行更改 /**1、配置TargetType協(xié)議可以一次性處理的參數(shù)- Todo: 根據(jù)自己的需要更改,不能統(tǒng)一處理的移除下面的代碼,并在APIManager中實現(xiàn)**/ 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、公共參數(shù)- Todo: 配置公共參數(shù),例如所有接口都需要傳token,version,time等,就可以在這里統(tǒng)一處理- Note: 接口傳參時可以覆蓋公共參數(shù)。下面的代碼只需要更改 【private var commonParams: [String: Any]?】**/ extension URLRequest {//TODO:處理公共參數(shù)private var commonParams: [String: Any]? {//所有接口的公共參數(shù)添加在這里: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 = requestreturn 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>)// 獲取分頁數(shù)據(jù)case getPageList(_ page:Int)// 獲取分組分頁數(shù)據(jù)case getGroupPageList(page:Int)// 獲取聯(lián)系人數(shù)據(jù)case getContact// 獲取微信運行排行榜case getWxMotionTops// 獲取固定數(shù)據(jù)case getSimpleArrDic///其他接口...case other1(p1: String, p2: Int, p3: String, p4: String)case other2}// MARK: - 補全【MoyaConfig 3:配置TargetType協(xié)議可以一次性處理的參數(shù)】中沒有處理的參數(shù) extension API: TargetType {//0. 基礎域名,整個項目只用一個,可以寫在MoyaConfig中var baseURL: URL {switch self {case .login:return URL(string:kBaseURL)!default:return URL(string:kBaseURL)!}}//1. 每個接口的相對路徑//請求時的絕對路徑是 baseURL + pathvar 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 .getcase.getContact,.getWxMotionTops,.getSimpleArrDic,.login:return .post}}//3. Task是一個枚舉值,根據(jù)后臺需要的數(shù)據(jù),選擇不同的http task。var task: Task {var params: [String: Any] = [:]switch self {case .login:return .requestPlaincase let .getPageList(page):params["page"] = pageparams["limit"] = 15params["maxCount"] = 100case let .other1(_, _, p3, p4):params["p3"] = p3params["p4"] = p4default://不需要傳參數(shù)的接口走這里return .requestPlain}return .requestParameters(parameters: params, encoding: URLEncoding.default)}}使用
// Alamofire + Moya + SwiftyJSON JhHttpTool.request(API.getPageList(1)) {[weak self] json inself?.mTextView.text = String(describing: JSON(json))JhAllLog(JSON(json)) } failure: {code, msg inJhLog("code : \(code!)")JhLog("message : \(msg)") }鏈式封裝Alamofire
鏈式封裝,API可以直接通過一個文件進行管理,里面只放url路徑
// // JhRequest.swift // JhSwiftDemo // // Created by Jh on 2021/12/28. // 鏈式網(wǎng)絡請求工具類:Alamofire + SwiftyJSON import Foundation import Alamofire import SwiftyJSONenum HttpRequestType {case getcase 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? // URLprivate var params: [String: Any]? // 參數(shù)private var success: SuccessHandlerType? // 成功的回調(diào)private var failure: FailureHandlerType? // 失敗的回調(diào)private var httpRequest: Request?}// NetworkKit屬性的設置 extension NetworkKit{/// 設置urlfunc url(_ url: String?) -> Self {self.url = urlreturn self}/// 設置post/get 默認postfunc requestType(_ type:HttpRequestType) -> Self {self.requestType = typereturn self}/// 設置參數(shù)func params(_ params: [String: Any]?) -> Self {self.params = paramsreturn self}/// 成功的回調(diào)func success(_ handler: @escaping SuccessHandlerType) -> Self {self.success = handlerreturn self}///失敗的回調(diào)func failure(handler: @escaping FailureHandlerType) -> Self {self.failure = handlerreturn self}}// NetworkKit請求相關(guān) extension NetworkKit{/// 發(fā)起請求 設置好相關(guān)參數(shù)后再調(diào)用func request() -> Void {var dataRequest: DataRequest? // alamofire請求后的返回值// 發(fā)起請求if let URLString = url {ProgressHUD.show()let method = requestType == .get ? HTTPMethod.get : HTTPMethod.postdataRequest = Alamofire.request(URLString, method: method, parameters: params)httpRequest = dataRequest}dataRequest?.responseJSON {(response) inProgressHUD.hide()switch response.result {case let .success(response):do {// *********** 這里可以統(tǒng)一處理錯誤碼,彈出提示信息 ***********let responseObject = JSON(response)let code = responseObject["code"].intValuelet msg = String(describing: responseObject["msg"])switch (code) {case 200 :// 數(shù)據(jù)返回正確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: 跳轉(zhuǎn)到登錄頁的操作:}}// 取消請求func cancel() {httpRequest?.cancel()}}使用
// 鏈式網(wǎng)絡請求:Alamofire + SwiftyJSONlet url = kBaseURL + "/getSimpleArrDic"JhRequest.url(url).params([:]).requestType(.post).success { res inJhLog(" ========鏈式網(wǎng)絡請求======== ")JhAllLog(res)JhAllLog(res["code"])}.failure { code, msg inJhLog("code : \(code!)")JhLog("message : \(msg)")}.request()總結(jié)
以上是生活随笔為你收集整理的Swift -《从0到1 - 5》:封装网络请求工具类(Alamofire + Moya + SwiftyJSON)和链式封装的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单片机毕业设计
- 下一篇: SwiftyJSON之使用分析