2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Swift网络请求 - RXSwift + PromiseKit + Moya

Swift网络请求 - RXSwift + PromiseKit + Moya

时间:2023-02-28 21:42:16

相关推荐

Swift网络请求 - RXSwift + PromiseKit + Moya

Moya是基于Alamofire网络框架上进行的封装,支持RXSwift

创建模型

import Foundation/// 实用泛行实现通用格式public struct ResponseData<T>: Codable where T: Codable {let code: Stringlet message: Stringlet data: T?}public struct Post: Codable {let id: Intlet title: Stringlet body: Stringlet userID: Int}extension Post {enum CodingKeys: String, CodingKey {case idcase titlecase bodycase userID = "userId" //自定义key}}

定义TargetType

import Moyapublic enum ServerApi {///定义请求接口参数case getNews(channel: String, start: Int, num: Int)case getPosts(id: Int)}/// 实现协议方法extension ServerApi: TargetType {public var baseURL: URL {#if DEVELOPreturn URL(string: "http://localhost:3000")!#elseif PREVIEWreturn URL(string: "http://localhost:3000")!#elsereturn URL(string: "http://localhost:3000")!#endif}/// 路径拼接public var path: String {switch self {case .getNews: ///获取新闻列表return "/getNews"case .getPosts:return "/posts"}}///请求方式public var method: Method {switch self {case .getNews:return .postcase .getPosts:return .get}}///编码格式public var sampleData: Data {return "".data(using: .utf8)!}/// 请求任务public var task: Task {switch self {case .getNews(let channel, let start, let num):let param: [String: Any] = ["channel":channel,"start":start,"num": num]return .requestParameters(parameters: param, encoding: URLEncoding.default)case .getPosts(let userId):let param: [String: Int] = ["userId":userId]return .requestParameters(parameters: param, encoding: URLEncoding.default)}}/// heder 可根据不同的接口设置不通的headerpublic var headers: [String : String]? {return nil}}

网络请求管理

import Moyaimport RxSwiftimport PromiseKit/// 服务器网络请求public struct APIProvider<Target: TargetType> {private let _disposeBag = DisposeBag()///定义网络请求发起着private let _privider = MoyaProvider<Target>(callbackQueue: .global(), session:{() -> Session in// 配置特殊网络需求let serverTrustManager = APIServerTrustManager()let interceptor = APIRequestInterceptor()let configuration = URLSessionConfiguration.defaultconfiguration.timeoutIntervalForRequest = 15 //设置请求超时时间configuration.headers = .defaultreturn Session(configuration: configuration,interceptor: interceptor,serverTrustManager: serverTrustManager,redirectHandler: nil,cachedResponseHandler: nil,eventMonitors: [])}(), plugins: [NetWorksActivityPlugin(),NetWorksLoggerPlugin()])/// 网络请求////// - Parameters:/// - target: API类型/// - observeOn: 发起请求的Scheduler/// - subscribeOn: 相应请求返回的Scheduler/// - retryCount: 发生错误时重试次数/// - Returns: 指定范型的Promisepublic func request<T: Codable>(targetType: Target,observeOn: ImmediateSchedulerType = ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global()),subscribeOn: ImmediateSchedulerType = MainScheduler.instance) -> Promise<T> {return Promise {seal in_privider.rx.request(targetType, callbackQueue: DispatchQueue.global()).observeOn(observeOn).map({try $0._storeServerTimeIntervalDistance()._catchRandomDomainFlag()._filterServerSuccessData().map(T.self) ///这里根据自己需要,可直接转成模型,或者使用mapJson,或mapString}).subscribeOn(subscribeOn).subscribe(onSuccess: {seal.fulfill($0)}, onError: {(error) inseal.reject(error)}).disposed(by: _disposeBag)}}}extension Response {/// 根据http返回 校准服务器时间fileprivate func _storeServerTimeIntervalDistance() throws -> Response {guard let serverTime = response?.allHeaderFields["Date"] as? String else {return self}let dateFormatter = DateFormatter().then {$0.locale = Locale(identifier: "en_US_POSIX")$0.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"}if let serverDate = dateFormatter.date(from: serverTime) {// 此处无需时区处理let timeIntervalDistance = Date().timeIntervalSince1970 - serverDate.timeIntervalSince1970print("请求时间差: \(timeIntervalDistance)")// SystemDataManager.shared.preinfoManager.timeIntervalDistance = timeIntervalDistance}return self}/// 根据http返回 处理特殊逻辑 如切换域名fileprivate func _catchRandomDomainFlag() -> Response {if [403, 502, 503].contains(statusCode) {// 有错误可以抛特殊异常 用与retryprint("http 错误码: \(statusCode)")}return self}/// 服务器返回数据格式错误fileprivate func _filterServerSuccessData() throws -> Response {do {let responseJson = try mapJSON(failsOnEmptyData: false)guard let dict = responseJson as? [String: Any] else {throw APIError.parseJsonError }if let error = _praseServerError(dict: dict) {throw error }return self} catch {throw error}}/// 服务器自定义错误private func _praseServerError(dict: [String: Any]) -> Error? {/**{code: "000000"message: "请求成功"data: {}}*/guard let code = dict["code"] as? String else {//跟自己服务器约定的返回值字段return APIError.parseStatusCodeTypeError}let message = dict["message"] as? String //跟服务器约定的message字段guard ["000000"].contains(code) else {// 有错误,根据与服务器约定code判断return APIError.serverDefineError(code: code,message: message ?? "服务器返回 returnCode:\(code)")}return nil}}

interceptor

import Alamofire/// 对request在发出前进行特殊处理public struct APIRequestInterceptor: RequestInterceptor {private let _prepare: ((URLRequest) -> URLRequest)?private let _willSend: ((URLRequest) -> Void)?init(prepare: ((URLRequest) -> URLRequest)? = nil, willSend:((URLRequest) -> Void)? = nil) {_prepare = prepare_willSend = willSend}public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (AFResult<URLRequest>) -> Void) {var request = _prepare?(urlRequest) ?? urlRequestrequest.setValue("iphoneX", forHTTPHeaderField: "User-Agent")request.setValue("ios", forHTTPHeaderField: "client-type")_willSend?(request)completion(.success(request))}}

服务器信任ServerTrustManager

import Alamofirepublic final class APIServerTrustManager: ServerTrustManager {init() {let allHostsMustBeEvaluated = falselet evaluators = ["": DisabledEvaluator()]super.init(allHostsMustBeEvaluated: allHostsMustBeEvaluated, evaluators: evaluators)}}

插件Plugin

网络知识器管理器

import UIKit/// 网络活动指示器public final class NetWorksIndicatorScheduler {public static let shared = NetWorksIndicatorScheduler()private init(){}private var _activityCount: Int = 0private lazy var _activityQueue = DispatchQueue(label: "ActivityIndicatorQueue")/// 加载网络活动指示器public func pushActivityIndicator() {_activityQueue.sync {[weak self] inguard let self = self else {return }self._activityCount += 1self._updateActivityIndicator()}}public func popActivityIndicator() {_activityQueue.sync {[weak self] inguard let self = self else {return }self._activityCount -= 1if self._activityCount < 0 {self._activityCount = 0}self._updateActivityIndicator()}}/// 更新状态private func _updateActivityIndicator() {DispatchQueue.main.async {UIApplication.shared.isNetworkActivityIndicatorVisible = self._activityCount > 0}}}

NetWorksActivityPlugin

import Moya/// 网络活动状态观察public final class NetWorksActivityPlugin: PluginType {/// 将要发送的时候开启public func willSend(_ request: RequestType, target: TargetType) {NetWorksIndicatorScheduler.shared.pushActivityIndicator()}/// 数据返回关闭public func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {NetWorksIndicatorScheduler.shared.popActivityIndicator()}}

网络活动日志统计插件

import Moya/// 网络活动日志统计public final class NetWorksLoggerPlugin: PluginType {public func willSend(_ request: RequestType, target: TargetType) {#if DEBUGprint(request.request?.url ?? "request error")print(request.request?.allHTTPHeaderFields ?? "")#endif}public func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {#if DEBUGswitch result {case .success(let response):let printString = response.request?.url?.absoluteString ?? "无法找到请求路径"print("success" + printString)case .failure(let error):print("error: " + error.errorDescription)}#endif}}

错误处理

MoyaError

import Moyaextension MoyaError {/// MoyaError错误描述public var errorMoyaDescription: String {switch self {case MoyaError.imageMapping:return "请求异常 (图片数据解析)."case MoyaError.jsonMapping:return "请求异常 (Json数据解析)."case MoyaError.stringMapping:return "请求异常 (字符串数据解析)."case MoyaError.objectMapping(let error, _):return error.errorDescriptioncase MoyaError.encodableMapping:return "请求异常 (Encodable Mapping)."case MoyaError.statusCode(let response):return ("请求失败,请重试! 错误码: " + "(\(response.statusCode))")case MoyaError.requestMapping:return "请求异常 (Request Mapping)"case MoyaError.parameterEncoding(let error):return "请求异常 (Parameter Encoding): \(error.errorDescription)"case MoyaError.underlying(_, _):return "请求异常 (Underlying)"}}}

自己服务器错误

import Foundation/// 自己服务器错误public enum APIError: Swift.Error {/// 解析Json格式错误case parseJsonError/// 解析服务器定义StatusCode格式错误case parseStatusCodeTypeError/// 服务器自定义错误case serverDefineError(code: String, message: String)}extension APIError {/// 自己服务器错误描述public var errorAPIDescription: String {switch self {case .parseJsonError:return "解析Json格式错误"case .parseStatusCodeTypeError:return "解析服务器定义StatusCode格式错误"case .serverDefineError(_, let message): //返回服务器定义的错误信息return message}}}

错误描述扩展

import Moyaextension Swift.Error {/// Swift.Error错误描述 兼容所有错误类型的描述public var errorDescription: String {if let moyaError = self as? MoyaError {return moyaError.errorMoyaDescription} else if let apiError = self as? APIError {return apiError.errorAPIDescription}else if let rpError = self as? ResourceProviderError {return rpError.errorRPDescription} else {return localizedDescription}}/// 是否是 Moya被取消的网络请求public var isMoyaCancledType: Bool {let result: Boolguard let moyaError = self as? MoyaError else {result = falsereturn result}switch moyaError {case .underlying(let err, _):result = (err as NSError).code == -999default:result = false}return result}}

发送请求

import UIKitimport RxSwiftimport PromiseKitimport Moyaclass MoyaViewController: BaseViewController {private let _provide = APIProvider<ServerApi>()override func viewDidLoad() {super.viewDidLoad()}}extension MoyaViewController {override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {moyaRequest()}func moyaRequest() {let requestPromis: Promise<ResponseData<[Post]>> = _provide.request(targetType: .getPosts(id: 1))requestPromis.ensure {//请求完成前}.done {(result) in//请求完成print("请求成功回调:",result,result.message)}.catch {(error) in//错误处理print("请求失败回调:",error)}}}

扩展,资源文件加载

import Foundationimport RxSwift/// ResourceProviderType 协议public protocol ResourceProviderType {associatedtype ModelType: Codablefunc fetchSync(name: String, type: String) -> Result<ModelType, ResourceProviderError>func fetchAsync(name: String, type: String, callbackQueue: DispatchQueue?, completionHandler: @escaping (_ result: Result<ModelType, ResourceProviderError>) -> Void)}/// 资源 Providerpublic struct ResourceProvider<ModelType: Codable>: ResourceProviderType {/// 同步获取public func fetchSync(name: String, type: String) -> Result<ModelType, ResourceProviderError> {do {guard let resourcePath = Bundle.main.path(forResource: name, ofType: type) else {throw ResourceProviderError.notFound }let decoder: JSONDecoder = JSONDecoder()let jsonData = try Data(contentsOf: URL(fileURLWithPath: resourcePath))guard jsonData.count < 1 else {let resultObj = try decoder.decode(ModelType.self, from: jsonData)return .success(resultObj)}if let emptyJSONObjectData = "{}".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(ModelType.self, from: emptyJSONObjectData) {return .success(emptyDecodableValue)} else if let emptyJSONArrayData = "[{}]".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(ModelType.self, from: emptyJSONArrayData) {return .success(emptyDecodableValue)} else {throw ResourceProviderError.empty}} catch {if let errorT = error as? ResourceProviderError {return .failure(errorT)} else {return .failure(ResourceProviderError.mapObjectFail(error: error))}}}/// 异步获取 默认在主线程返回数据public func fetchAsync(name: String, type: String, callbackQueue: DispatchQueue?, completionHandler: @escaping (Result<ModelType, ResourceProviderError>) -> Void) {let callbackQueueT = callbackQueue ?? DispatchQueue.mainDispatchQueue.global().async {do {guard let resourcePath = Bundle.main.path(forResource: name, ofType: type) else {throw ResourceProviderError.notFound }let decoder: JSONDecoder = JSONDecoder()let jsonData = try Data(contentsOf: URL(fileURLWithPath: resourcePath))guard jsonData.count < 1 else {let resultObj = try decoder.decode(ModelType.self, from: jsonData)callbackQueueT.async {completionHandler(.success(resultObj)) }return}if let emptyJSONObjectData = "{}".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(ModelType.self, from: emptyJSONObjectData) {callbackQueueT.async {completionHandler(.success(emptyDecodableValue)) }} else if let emptyJSONArrayData = "[{}]".data(using: .utf8), let emptyDecodableValue = try? decoder.decode(ModelType.self, from: emptyJSONArrayData) {callbackQueueT.async {completionHandler(.success(emptyDecodableValue)) }} else {throw ResourceProviderError.empty}return} catch {if let errorT = error as? ResourceProviderError {callbackQueueT.async {completionHandler(.failure(errorT)) }} else {callbackQueueT.async {completionHandler(.failure(.mapObjectFail(error: error))) }}return}}}}// MARK: - Rx支持extension ResourceProvider: ReactiveCompatible {}extension Reactive where Base: ResourceProviderType {public func fetch(name: String, type: String) -> Single<Base.ModelType> {return Single.create {single inself.base.fetchAsync(name: name, type: type, callbackQueue: nil, completionHandler: {result inswitch result {case let .success(model):single(.success(model))case let .failure(error):single(.error(error))}})return Disposables.create()}}}

资源错误处理

import Foundation/// 资源错误public enum ResourceProviderError: Swift.Error {/// 找不到资源case notFound/// 解析 Json Object失败case mapObjectFail(error: Error)/// 资源为空case empty}extension ResourceProviderError {/// 资源错误描述public var errorRPDescription: String {switch self {case .notFound:return "找不到该资源"case .empty:return "资源为空"case .mapObjectFail(let error):return error.errorDescription}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。