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 Moya
import RxSwift
import 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 UIKit
import RxSwift
import PromiseKit
import Moya
class 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 Foundation
import 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)
}/// 资源 Provider
public 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}}
}

Swift网络请求 - RXSwift + PromiseKit + Moya相关推荐

  1. Alamofire网络库基础教程:使用 Alamofire 轻松实现 Swift 网络请求

    Alamofire网络库基础教程:使用 Alamofire 轻松实现 Swift 网络请求 转自 http://www.cocoachina.com/ios/20141202/10390.html 本 ...

  2. Swift 网络请求 Moya+RxSwift

    Swift中优雅的网络请求 官方github // 1.定一个enum enum MyService {xxxxcase showUser(id: Int)xxx }// 2.扩展这个enum,符合 ...

  3. Swift 网络请求库Moya的使用

    Moya是Swift中的网络库Alamofire的二次封装,Alamofire本身使用起来是很简单方便的,例子如下: func loadData(){var param = [String:Strin ...

  4. 使用 Siesta 处理 Swift 网络请求

    (原文地址:https://medium.freecodecamp.o...) 今天我跟大家分享一下我的 iOS 网络库新欢,名字叫做 Siesta."她有啥特殊的?为啥我不直接用 Almo ...

  5. [译]使用 Siesta 处理 Swift 网络请求

    (原文地址:https://medium.freecodecamp.org/swift-networking-with-siesta-5b5e7089bd8f) 今天我跟大家分享一下我的 iOS 网络 ...

  6. Swift 网络请求数据与解析

    一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...

  7. 二、Swift网络请求回来的数据我这样取

    网络请求框架Alamofire 源码地址 Swift 2.3 Alamofire3.0版本支持 iOS 8 Swift 3 Alamofire4.0以上版本支持 iOS 9及以上系统 json数据: ...

  8. swift 网络请求中含有特殊字符的解决方案

    在网络请求时,URL出现中文或特殊字符时会造成请求失败,通常可使用  addingPercentEncoding(withAllowedCharacters: CharacterSet) 方法进行解决 ...

  9. Swift 优雅的网络请求Moya

    Moya使用demo Moya 面向协议 Moya的模块组成 代码demo Controller中使用 Moya Moya是一个网络抽象层,它在底层将Alamofire进行封装,对外提供更简洁的接口供 ...

  10. Swift 掌控Moya的网络请求、数据解析与缓存

    Moya 在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的 Response 为 nil,这时还得去解析相应的 Error Codable 可以帮助我们快速的解析数 ...

最新文章

  1. Cissp-【第4章 通信与网络安全】-2021-3-14(543页-560页)
  2. Android之6.0上的重要变化(一)
  3. python列表生成多个号码_python遍历多个列表生成列表或字典
  4. mysql模糊查询如果是_mysql模糊查询的一个问题
  5. vmware workstations 共享虚拟机 failed to connect 903
  6. 五日均线指标编程案例讲解
  7. 数据库常考题型(8)——将关系模式R分解成2NF
  8. python判断字符串间的包含关系
  9. 25,UC(04) .
  10. 怎样搭建企业内部知识库
  11. 4.3 走出应试的阴影——《逆袭大学》连载
  12. jdk动态代理proxy
  13. C#网络编程 - 局域网聊天室(UDP)
  14. Linux中miniconda的操作
  15. UIL(单例框架)Android-Universal-Image-Loader (图片加载框架)
  16. html_css_flex弹性盒模型_ZHOU125disorder_
  17. 关于Typecho的主题魔改记录
  18. 北师大 外国教育史-3(文艺复兴时期的教育、宗教改革时期的教育)
  19. 各大银行软件研发中心
  20. Java在控制台输出带颜色字符的两种方式

热门文章

  1. NR: PointA,offsetToPonitA,kSSB三者关系。
  2. LOJ.6073.[2017山东一轮集训Day5]距离(可持久化线段树 树链剖分)
  3. 谷歌 AI 团队用 GAN 模型合成异形生物体
  4. 3-arm裸机存储器控制器之SDRAM
  5. 年度回顾篇:2018年的亚马逊,众生虽苦,诸恶莫作
  6. 【html】css样式
  7. JSP隐式对象——out对象、pageContext对象、exception对象
  8. 如何修复Windows 10中最烦人的东西
  9. CSMA/CD总线以太网和交换式以太网
  10. 实现自己的Protobuf Any