iOS 使用Moya网络请求的实现方法

由于前段时间写了这篇文章,最新Moya已更新最新版本,故此也更新了下用法,本人已使用,故特意奉上最新的使用demo供参考。Moya11.0.2Demo

Moya简介

Moya是你的 app 中缺失的网络层。不用再去想在哪儿(或者如何)安放网络请求,Moya 替你管理。

Moya有几个比较好的特性:

  • 编译时检查正确的API端点访问.
  • 使你定义不同端点枚举值对应相应的用途更加明晰.
  • 提高测试地位从而使单元测试更加容易.

Swift我们用 Alamofire 来做网络库.而 Moya在Alamofire的基础上又封装了一层,如下流程图说明 Moya 的简单工作流程图:

Moya的官方下载地址点我强大的Moya,有具体的使用方法在demo里面有说明。

本文主要介绍一下 Moya 的用法

  • 设置请求头部信息 设
  • 置超时时间
  • 自定义插件
  • 自签名证书

注意:以下所出现的 NetAPIManager 跟官网上demo的** GitHub**是一样类型的文件,都是这个enum实现一个协议TargetType,点进去可以看到TargetType定义了我们发送一个网络请求所需要的东西,什么baseURL,parameter,method等一些计算性属性,我们要做的就是去实现这些东西,当然有带默认值的我们可以不去实现,但是设置头部信息跟超时时间就要修改这些系统默认设置了。

为了看得更加清楚,贴上 NetAPIManager 文件的内容

//
// NetAPIManager.swift
// NN110
//
// Created by 陈亦海 on 2017/5/12.
// Copyright © 2017年 陈亦海. All rights reserved.
//

import Foundation
import Moya

enum NetAPIManager {
 case Show
 case upload(bodyData: Data)
 case download
 case request(isTouch: Bool, body: Dictionary<String, Any>? ,isShow: Bool)
}

extension NetAPIManager: TargetType {
 var baseURL: URL {//服务器地址

  switch self {
  case .request( _, _, _):
   return URL(string: "https://www.pmphmall.com")!
  default:
   return URL(string: "https://httpbin.org")!
  }

 }

 var path: String {//具体某个方法的路径
  switch self {
  case .Show:
   return ""
  case .upload(_):
   return ""
  case .request(_, _, _):
   return "/app/json.do"
  case .download:
   return ""
  }
 }

 var method: Moya.Method {//请求的方法 get或者post之类的
  switch self {
  case .Show:
   return .get
  case .request(_, _, _):
   return .post
  default:
   return .post
  }
 }

 var parameters: [String: Any]? {//请求的get post给服务器的参数
  switch self {
  case .Show:
   return nil
  case .request(_, _, _):
   return ["msg":"H4sIAAAAAAAAA11SSZJFIQi7EqPAEgTvf6TP62W7sMoSQhKSWDrs6ZUKVWogLwYV7RjHFBZJlNlzloN6LVqID4a+puxqRdUKVNLwE1TRcZIC/fjF2rPotuXmb84r1gMXbiASZIZbhQdKEewJlz41znDkujCHuQU3dU7G4/PmVRnwArMLXukBv0J23XVahNO3VX35wlgce6TLUzzgPQJFuHngAczl6VhaNXpmRLxJBlMml6gdLWiXxTdO7I+iEyC7XuTirCQXOk4dotgArgkH/InxVjfNTnE/uY46++hyAiLFuFL4cv1Z8WH5DgB2GnvFXMh5gm53Tr13vqqrEYtcdXfkNsMwKB+9sAQ77grNJmquFWOhfXA/DELlMB0KKFtHOc/ronj1ml+Z7qas82L3VWiCVQ+HEitjTVzoFw8RisFN/jJxBY4awvq427McXqnyrfCsl7oeEU6wYgW9yJtj1lOkx0ELL5Fw4z071NaVzRA9ebxWXkFyothgbB445cpRmTC+//F73r1kOyQ3lTpec12XNDR00nnq5/YmJItW3+w1z27lSOLqgVctrxG4xdL9WVPdkH1tkiZ/pUKBGhADAAA="]
  default:
   return nil

  }
 }

 var sampleData: Data { //编码转义
  return "{}".data(using: String.Encoding.utf8)!
 }

 var task: Task { //一个请求任务事件

  switch self {

  case let .upload(data):
  return .upload(.multipart([MultipartFormData(provider: .data(data), name: "file", fileName: "gif.gif", mimeType: "image/gif")]))

  default:
   return .request

  }

  }

 var parameterEncoding: ParameterEncoding {//编码的格式
  switch self {
  case .request(_, _, _):
   return URLEncoding.default
  default:
   return URLEncoding.default
  }

 }
 //以下两个参数是我自己写,用来控制网络加载的时候是否允许操作,跟是否要显示加载提示,这两个参数在自定义插件的时候会用到
 var touch: Bool { //是否可以操作

  switch self {
  case .request(let isTouch, _, _):
   return isTouch
  default:
   return false
  }

 }

 var show: Bool { //是否显示转圈提示

  switch self {
  case .request( _, _,let isShow):
   return isShow
  default:
   return false
  }

 }

}

如何设置Moya请求头部信息

头部信息的设置在开发过程中很重要,如服务器生成的token,用户唯一标识等 我们直接上代码,不说那么多理论的东西,哈哈

// MARK: - 设置请求头部信息
let myEndpointClosure = { (target: NetAPIManager) -> Endpoint<NetAPIManager> in

 let url = target.baseURL.appendingPathComponent(target.path).absoluteString
 let endpoint = Endpoint<NetAPIManager>(
  url: url,
  sampleResponseClosure: { .networkResponse(200, target.sampleData) },
  method: target.method,
  parameters: target.parameters,
  parameterEncoding: target.parameterEncoding
 )

 //在这里设置你的HTTP头部信息
 return endpoint.adding(newHTTPHeaderFields: [
  "Content-Type" : "application/x-www-form-urlencoded",
  "ECP-COOKIE" : ""
  ])

}

如何设置请求超时时间

// MARK: - 设置请求超时时间
let requestClosure = { (endpoint: Endpoint<NetAPIManager>, done: @escaping MoyaProvider<NetAPIManager>.RequestResultClosure) in

 guard var request = endpoint.urlRequest else { return }

 request.timeoutInterval = 30 //设置请求超时时间
 done(.success(request))
}

自定义插件

自定义插件必须 PluginType 协议的两个方法willSend与didReceive

//
// MyNetworkActivityPlugin.swift
// NN110
//
// Created by 陈亦海 on 2017/5/10.
// Copyright © 2017年 CocoaPods. All rights reserved.
//

import Foundation
import Result
import Moya

/// Network activity change notification type.
public enum MyNetworkActivityChangeType {
 case began, ended
}

/// Notify a request's network activity changes (request begins or ends).
public final class MyNetworkActivityPlugin: PluginType {

 public typealias MyNetworkActivityClosure = (_ change: MyNetworkActivityChangeType, _ target: TargetType) -> Void
 let myNetworkActivityClosure: MyNetworkActivityClosure

 public init(newNetworkActivityClosure: @escaping MyNetworkActivityClosure) {
  self.myNetworkActivityClosure = newNetworkActivityClosure
 }

 // MARK: Plugin

 /// Called by the provider as soon as the request is about to start
 public func willSend(_ request: RequestType, target: TargetType) {
  myNetworkActivityClosure(.began,target)
 }

 /// Called by the provider as soon as a response arrives, even if the request is cancelled.
 public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
  myNetworkActivityClosure(.ended,target)
 }
}

使用自定义插件方法

// MARK: - 自定义的网络提示请求插件
let myNetworkPlugin = MyNetworkActivityPlugin { (state,target) in
 if state == .began {
  //  SwiftSpinner.show("Connecting...")

  let api = target as! NetAPIManager
  if api.show {
   print("我可以在这里写加载提示")
  }

  if !api.touch {
   print("我可以在这里写禁止用户操作,等待请求结束")
  }

  print("我开始请求\(api.touch)")

  UIApplication.shared.isNetworkActivityIndicatorVisible = true
 } else {
  //  SwiftSpinner.show("request finish...")
  //  SwiftSpinner.hide()
  print("我结束请求")
  UIApplication.shared.isNetworkActivityIndicatorVisible = false

 }
}

自签名证书

在16年的WWDC中,Apple已表示将从2017年1月1日起,**所有新提交的App必须强制性应用HTTPS协议来进行网络请求。**默认情况下非HTTPS的网络访问是禁止的并且不能再通过简单粗暴的向Info.plist中添加NSAllowsArbitraryLoads 设置绕过ATS(App Transport Security)的限制(否则须在应用审核时进行说明并很可能会被拒)。所以还未进行相应配置的公司需要尽快将升级为HTTPS的事项提上进程了。本文将简述HTTPS及配置数字证书的原理并以配置实例和出现的问题进行说明,希望能对你提供帮助。(比心~)

HTTPS: 简单来说,HTTPS就是HTTP协议上再加一层加密处理的SSL协议,即HTTP安全版。相比HTTP,HTTPS可以保证内容在传输过程中不会被第三方查看、及时发现被第三方篡改的传输内容、防止身份冒充,从而更有效的保证网络数据的安全。 HTTPS客户端与服务器交互过程: 1、 客户端第一次请求时,服务器会返回一个包含公钥的数字证书给客户端; 2、 客户端生成对称加密密钥并用其得到的公钥对其加密后返回给服务器; 3、 服务器使用自己私钥对收到的加密数据解密,得到对称加密密钥并保存; 4、 然后双方通过对称加密的数据进行传输。

数字证书: 在HTTPS客户端与服务器第一次交互时,服务端返回给客户端的数字证书是让客户端验证这个数字证书是不是服务端的,证书所有者是不是该服务器,确保数据由正确的服务端发来,没有被第三方篡改。数字证书可以保证数字证书里的公钥确实是这个证书的所有者(Subject)的,或者证书可以用来确认对方身份。证书由公钥、证书主题(Subject)、数字签名(digital signature)等内容组成。其中数字签名就是证书的防伪标签,目前使用最广泛的SHA-RSA加密。 证书一般分为两种:

  1. 一种是向权威认证机构购买的证书,服务端使用该种证书时,因为苹果系统内置了其受信任的签名根证书,所以客户端不需额外的配置。为了证书安全,在证书发布机构公布证书时,证书的指纹算法都会加密后再和证书放到一起公布以防止他人伪造数字证书。而证书机构使用自己的私钥对其指纹算法加密,可以用内置在操作系统里的机构签名根证书来解密,以此保证证书的安全。
  2. 另一种是自己制作的证书,即自签名证书。好处是不需要花钱购2买,但使用这种证书是不会受信任的,所以 需要我们在代码中将该证书配置为信任证书.

自签名证书具体实现: 我们在使用自签名证书来实现HTTPS请求时,因为不像机构颁发的证书一样其签名根证书在系统中已经内置了,所以我们需要在App中内置自己服务器的签名根证书来验证数字证书。首先将服务端生成的.cer格式的根证书添加到项目中,注意在添加证书要一定要记得勾选要添加的targets。

这里有个地方要注意 :苹果的ATS要求服务端必须支持TLS 1.2或以上版本;必须使用支持前向保密的密码;证书必须使用SHA-256或者更好的签名hash算法来签名,如果证书无效,则会导致连接失败。由于我在生成的根证书时签名hash算法低于其要求,在配置完请求时一直报 NSURLErrorServerCertificateUntrusted = -1202错误,希望大家可以注意到这一点。

那么如何在Moya中使用自签名的证书来实现HTTPS网络请求呢,请期待下回我专门分享......需要自定义一个Manager管理

综合使用的方法如下

定义一个公用的Moya请求服务对象

let MyAPIProvider = MoyaProvider<NetAPIManager>(endpointClosure: myEndpointClosure,requestClosure: requestClosure, plugins: [NetworkLoggerPlugin(verbose: true, responseDataFormatter: JSONResponseDataFormatter),myNetworkPlugin])

// MARK: -创建一个Moya请求
func sendRequest(_ postDict: Dictionary<String, Any>? = nil,
     success:@escaping (Dictionary<String, Any>)->(),
     failure:@escaping (MoyaError)->()) -> Cancellable? {

 let request = MyAPIProvider.request(.Show) { result in
  switch result {
  case let .success(moyaResponse):

   do {
    let any = try moyaResponse.mapJSON()
    let data = moyaResponse.data
    let statusCode = moyaResponse.statusCode
    MyLog("\(data) --- \(statusCode) ----- \(any)")

    success(["":""])

   } catch {

   }

  case let .failure(error):

   print(error)
   failure(error)
  }
 }

 return request
}

取消所有的Moya请求

// MARK: -取消所有请求
func cancelAllRequest() {
// MyAPIProvider.manager.session.invalidateAndCancel() //取消所有请求
 MyAPIProvider.manager.session.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
  dataTasks.forEach { $0.cancel() }
  uploadTasks.forEach { $0.cancel() }
  downloadTasks.forEach { $0.cancel() }
 }

 //let sessionManager = Alamofire.SessionManager.default
 //sessionManager.session.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
 // dataTasks.forEach { $0.cancel() }
 // uploadTasks.forEach { $0.cancel() }
 // downloadTasks.forEach { $0.cancel() }
 //}

}

完毕,待续更高级的用法...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS当多个网络请求完成后执行下一步的方法详解

    前言 在开发中,我们很容易遇到这样的需求,需要我们同时做多个网络请求,所有网络请求都完成后才能进行下一步的操作. 网络请求的任务是提交给子线程异步处理了,网络请求这样的任务也就快速执行完毕了,但是网络请求是一个任务,处理收到的网络响应又是一个任务,注意不要把这两个过程混为一谈. 如下载多个图片,下载完了才能展示,今天我们就来研究一下这个问题的解决方案. 解决方法 1.首先,我们创建一个项目,然后做一般性的做法,不做任何处理去连续请求一个接口10次: 先在viewDidLoad中创建第一种情况.

  • 详解iOS中多个网络请求的同步问题总结

    场景描述:我们同时发出了a.b.c 3个网络请求,我们希望在a.b.c 3个网络请求都结束的时候获得一个通知. 常见解决方法:通过度娘目前找到两种做法:1.通过添加标识来判断请求是否全部结束 2.dispatch_group + 信号量 本篇文章demo 1.添加标识的解决方法 在遇到这个问题时首先想到了唐巧大大的猿题库团队开源的网络框架YTKNetwork,然后阅读源码发现YTKNetwork是通过添加标识来实现网络请求的批量请求处理: 话不多说直接上代码在YTKNetwork里负责进行网络批

  • node.js通过axios实现网络请求的方法

    1.使用Npm 下载axios npm install --save axios var update_url = axios.create({ baseURL:'debug url' }); update_url.get('/debug url').then(function (response){ //response 就是请求url 返回的内容 } 上述的方法请求文件时候,body的默认格式不是form-data.因此我们需要请求的数据格式为form-data的时候,需要使用下面的库 re

  • 详解iOS AFNetworking取消正在进行的网络请求

    简介 项目开发时,开发人员经常会遇到一种情况,A控制器push进入B控制器,B控制器正在进行网络请求,请求未结束时,点击返回回到A控制器,现在问题出现了,B中网络请求还在执行,dealloc并未立即调用,为什么会发生这种情况?想在退出当前控制器时取消掉正在进行的请求,怎么做? 网络请求的封装 以AFNetworking为例,上我自己的网络请求封装主要代码: //单例模式 + (HttpManager *)sharedManager { static dispatch_once_t once; d

  • IOS开发中异步网络请求上实现同步逻辑

    IOS开发中异步网络请求上实现同步逻辑 前提: 可能遇到一些问题,比如上传多个数据,需要等多个数据上传成功后做一定的处理,而且一个个上传,万一哪个上传失败了,后面就不需要上传了,直接报错. 之前ASI的网络库中是有同步请求的接口,所以很好处理,AFNetwork的网络库只有异步的网络请求,该怎么实现呢? 1.循环异步拼组 - (void)uploadFile:(NSArray *)imageArray atIndex:(NSInteger)index imagesCount:(NSInteger

  • 详解如何拦截iOS所有网络请求

    背景 最近在研究iOS无埋点统计技术,我们的统计SDK主要分两部分:点击事件和网络请求.统计所有的点击事件是采用Method Swizzling实现的,可以做到使用中不需要一行代码实现统计所有事件,具体细节将来我会专门抽几篇文章介绍. 今天主要说说如何统计APP中的所有网络请求.公司网络请求如果不是静态库或者框架,很容易想到在网络请求发送和返回时添加统计的代码.如何在不修改原来代码(或者修改最少)的基础上拦截所有的请求呢,能不能从系统层面上拦截回调呢?答案是肯定的,苹果有一个黑魔法NSURLPr

  • iOS在页面销毁时如何优雅的cancel网络请求详解

    前言 大家都知道,当一个网络请求发出去之后,如果不管不顾,有可能出现以下情况: 进入某个页面,做了某种操作(退出页面.切换某个tab等等)导致之前的请求变成无用请求,这时候有可能出现虽然页面已经销毁了,但是网络请求还在外面飞的情况,如果放任不管,那么这个请求既浪费流量,又浪费性能,尤其是在网络比较差时,一个超时的无用请求更让人不爽.这时候,我们最好的办法是cancel掉这些无用的请求. 传统的cancel方式是这样的: 1.在类里面需要持有请求对象 @property (strong/weak,

  • IOS 网络请求中设置cookie

    IOS 网络请求中设置cookie 1. ASIHTTPRequest ASIHTTPRequest 是一款极其强劲的 HTTP 访问开源项目.让简单的 API 完成复杂的功能,如:异步请求,队列请求,GZIP 压缩,缓存,断点续传,进度跟踪,上传文件,HTTP 认证. cookie的支持 如果 Cookie 存在的话,会把这些信息放在 NSHTTPCookieStorage 容器中共享,并供下次使用.你可以用 [ ASIHTTPRequest setSessionCookies:nil ] ;

  • iOS中多网络请求的线程安全详解

    前言 在iOS 网络编程有一种常见的场景是:我们需要并行处理二个请求并且在都成功后才能进行下一步处理.下面是部分常见的处理方式,但是在使用过程中也很容易出错: DispatchGroup:通过 GCD 机制将多个请求放到一个组内,然后通过 DispatchGroup.wait() 和 DispatchGroup.notify() 进行成功后的处理. OperationQueue:为每一个请求实例化一个 Operation 对象,然后将这些对象添加到 OperationQueue ,并且根据它们之

  • iOS 使用Moya网络请求的实现方法

    由于前段时间写了这篇文章,最新Moya已更新最新版本,故此也更新了下用法,本人已使用,故特意奉上最新的使用demo供参考.Moya11.0.2Demo Moya简介 Moya是你的 app 中缺失的网络层.不用再去想在哪儿(或者如何)安放网络请求,Moya 替你管理. Moya有几个比较好的特性: 编译时检查正确的API端点访问. 使你定义不同端点枚举值对应相应的用途更加明晰. 提高测试地位从而使单元测试更加容易. Swift我们用 Alamofire 来做网络库.而 Moya在Alamofir

  • iOS判断网络请求超时的方法

    本文介绍了iOS判断网络请求超时的方法,代码具体如下: + (AFHTTPRequestOperation *)requestOperationWithUrl:(NSString *)url requetMethod:(NSString *)method paramData:(NSDictionary *)aParamData constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block success

  • iOS开发TableView网络请求及展示预加载实现示例

    目录 引言 传统的上拉加载更多 滑动过程中预加载 方法1(最简单.高效和最推荐): 方法2(自己计算实现,不推荐了): 下面是具体实现细节(此细节是针对上述方法2的) 总结 引言 2022.02.11更新:新增了最简单.高效和最推荐的方法. 2020.05.25更新:对总结进行了详细的补充. 传统的上拉加载更多 在iOS的开发过程中,如果用列表展示数据,此时一般的逻辑为上拉加载更多数据,配合MJRefresh就是在滑动到最底部时,触发加载更多的网络请求. 滑动过程中预加载 如果希望体验好一点,那

  • iOS获取当前网络环境的实现方法(推荐)

    实例如下: // 获取网络环境的方法 + (NSString *)networktype{ NSArray *subviews = [[[[UIApplication sharedApplication] valueForKey:@"statusBar"] valueForKey:@"foregroundView"]subviews]; NSNumber *dataNetworkItemView = nil; for (id subview in subviews)

  • android 网络请求库volley方法详解

    使用volley进行网络请求:需先将volley包导入androidstudio中 File下的Project Structrue,点加号导包 volley网络请求步骤: 1. 创建请求队列       RequestQueue queue = Volley.newRequestQueue(this); 2.创建请求对象(3种) StringRequest request = new StringRequest("请求方法","请求的网络地址","成功的网

  • mac 安装python网络请求包requests方法

    如下所示: sudo easy_install requests 出现如图所示信息 done 即可愉快的使用 requests了 以上这篇mac 安装python网络请求包requests方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • 浅谈IOS中AFNetworking网络请求的get和post步骤

    1.首先通过第三方:CocoaPods下载AFNetworking 1.1.先找到要查找的三方库:pod search + AFNetworking 1.2.出来一堆列表页面,选择三方库最新版本命令,例如: pod 'MBProgressHUD','~>0.8'  (:q 返回) 1.3.创建工程,进入工程: cd + 工程路径 1.4.编辑工程的Podfile文件: vim Podfile 1.5.(platform :iOS, '8.0'
target "工程名" do
po

  • Angular网络请求的封装方法

    很多时候,我很喜欢angular的编码风格,特别是angular支持typescript之后,完整的生命周期,完美的钩子函数,都是别的语言所无法替代的. 这里我来说说我自己的网络请求封装,某种意义上来说,angular自己的网络请求封装的很好的,我们没有必要再来画蛇添足,但是,可能是我有那么一点点的代码洁癖吧,喜欢自己的风格样式,所以就有了这一点多余的东西. Angular的网络请求 这里是angular自己的网络请求. url代表网络请求地址, param网络请求参数 网络请求配置,例如:请求

  • 用Fundebug插件记录网络请求异常的方法

    在服务端,不管我们使用Node.js.Java.PHP还是Python等等,都会用日志以文本的形式记录请求以及报错信息.这个对于后端做事后分析是很有用的. 另一方面,前端有时候出问题其实是因为后端接口报错,返回数据异常导致.而实际上,前端才是用户直接触及的端,所以出了问题,首先是在前端体现出来,首先也是找前端. 为了更好地定位问题是前端代码还是接口问题,在这里推荐使用Fundebug的前端JavaScript监控插件.该插件从0.1.0之后,就开始支持HTTP请求错误的监控. 例子 为了测试,我

  • RxJava+Retrofit实现网络请求封装的方法

    简要介绍 Retrofit是当前应用非常广泛的网络请求框架,通常结合RxJava来进行网络请求,本文将展示一个采用RxJava+Retrofit的网络请求demo. 集成步骤 1.app工程的build.gradle中添加依赖 //retrofit2 implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.

随机推荐