在Android开发中有非常强大的 Retrofit 请求,结合RxJava可以非常方便实现 RESTful API 网络请求。在 iOS开发中也有非常强大的网络请求库 Moya ,Moya是一个基于 Alamofire 开发的,轻量级的Swift网络层。Moya的可扩展性非常强,可以方便和RXSwift、ObjectMapper结合。

测试 REST API 定义

我们先用服务端定义几个REST API,开发者根据自己的条件来实现。

请求错误格式实例
  1. {
  2. "error": "密码错误",
  3. "error_code": "password_error"
  4. }
测试 API 列表
  1. http://127.0.0.1:8080/account/login,参数username、password,post请求,成功响应为User。
  2. http://127.0.0.1:8080/user/{userId},get请求,成功响应为User。
  3. http://127.0.0.1:8080/user/query?q={keyword},get请求,成功响应为User列表。

创建接口

  1. // MyApiService.swift
  2. import Moya
  3. enum MyApiService {
  4. case login(username:String,password:String)
  5. case user(userId:String)
  6. case userQuery(keyword:String)
  7. }
  8. extension MyApiService:TargetType{
  9. // 定义请求的host
  10. var baseURL: URL {
  11. return URL(string: "http://127.0.0.1:8080")!
  12. }
  13. // 定义请求的路径
  14. var path: String {
  15. switch self {
  16. case .login(_, _):
  17. return "/account/login"
  18. case .user(let userId):
  19. return "user/\(userId)"
  20. case .userQuery(_):
  21. return "user/query"
  22. }
  23. }
  24. // 定义接口请求方式
  25. var method: Moya.Method {
  26. switch self {
  27. case .login:
  28. return .post
  29. case .user,.userQuery:
  30. return .get
  31. }
  32. }
  33. // 定义模拟数据
  34. var sampleData: Data {
  35. switch self {
  36. case .login(let username, _):
  37. return "{\"username\": \"\(username)\", \"id\": 100}".data(using: String.Encoding.utf8)!
  38. case .user(_):
  39. return "{\"username\": \"Wiki\", \"id\": 100}".data(using: String.Encoding.utf8)!
  40. case .userQuery(_):
  41. return "{\"username\": \"Wiki\", \"id\": 100}".data(using: String.Encoding.utf8)!
  42. }
  43. }
  44. // 构建参数
  45. var task: Task {
  46. switch self {
  47. case .login(let username, let passowrd):
  48. return .requestParameters(parameters: ["username": username,"passowrd": passowrd], encoding: URLEncoding.default)
  49. case .user(_):
  50. return .requestPlain
  51. case .userQuery(let keyword):
  52. return .requestParameters(parameters: ["keyword": keyword], encoding: URLEncoding.default)
  53. }
  54. }
  55. // 构建请求头部
  56. var headers: [String : String]? {
  57. return ["Content-type": "application/json"]
  58. }
  59. }

请求数据

  1. let provider = MoyaProvider<MyApiService>()
  2. // Moya 提供最原始的请求方式,响应的数据是二进制
  3. provider.request(.user(userId: "101")){ result in
  4. // do something with the result
  5. let text = String(bytes: result.value!.data, encoding: .utf8)
  6. print("text1 = \(text)")
  7. }
  8. // 结合RxSwift,响应的数据是二进制
  9. provider.rx.request(.user(userId: "101")).subscribe({result in
  10. // do something with the result
  11. switch result {
  12. case let .success(response):
  13. let text = String(bytes: response.data, encoding: .utf8)
  14. print("text2 = \(text)")
  15. case let .error(error):
  16. print(error)
  17. }
  18. })
  19. // 通过mapJSON把数据转换成json格式
  20. provider.rx.request(.user(userId: "101")).mapJSON().subscribe({result in
  21. // do something with the result
  22. switch result {
  23. case let .success(text):
  24. print("text3 = \(text)")
  25. case let .error(error):
  26. print(error)
  27. }
  28. })
  29. // 通过mapJSON把数据转换成json格式,并转换成最常见的Observable
  30. provider.rx.request(.user(userId: "101")).mapJSON().asObservable().subscribe(onNext: { result in
  31. // do something with the result
  32. print("text4 = \(result)")
  33. }, onError:{ error in
  34. // do something with the error
  35. })
请求数据:RxBlocking

RxBlocking使用教程 ,可以使用同步的方式请求网络

  1. import RxBlocking
  2. do{
  3. let text = try provider.rx.request(.user(userId: "101")).mapJSON().toBlocking().first()
  4. print("text5 = \(text)")
  5. }catch{
  6. print(error)
  7. }

结合 ObjectMapper

引入ObjectMapper
  1. pod 'ObjectMapper', '~> 3.4'
编写RxSwift拓展代码
  1. // MoyaRxSwiftObjectMapperExtension.swift
  2. import Foundation
  3. import RxSwift
  4. import Moya
  5. import ObjectMapper
  6. public extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
  7. func mapObject<T: BaseMappable>(type: T.Type) -> Single<T> {
  8. return self.map{ response in
  9. return try response.mapObject(type: type)
  10. }
  11. }
  12. func mapArray<T: BaseMappable>(type: T.Type) -> Single<[T]> {
  13. return self.map{ response in
  14. return try response.mapArray(type: type)
  15. }
  16. }
  17. }
  18. public extension ObservableType where E == Response {
  19. func mapObject<T: BaseMappable>(type: T.Type) -> Observable<T> {
  20. return self.map{ response in
  21. return try response.mapObject(type: type)
  22. }
  23. }
  24. func mapArray<T: BaseMappable>(type: T.Type) -> Observable<[T]> {
  25. return self.map{ response in
  26. return try response.mapArray(type: type)
  27. }
  28. }
  29. }
  30. public extension Response{
  31. func mapObject<T: BaseMappable>(type: T.Type) throws -> T{
  32. let text = String(bytes: self.data, encoding: .utf8)
  33. if self.statusCode < 400 {
  34. return Mapper<T>().map(JSONString: text!)!
  35. }
  36. do{
  37. let serviceError = Mapper<ServiceError>().map(JSONString: text!)
  38. throw serviceError!
  39. }catch{
  40. if error is ServiceError {
  41. throw error
  42. }
  43. let serviceError = ServiceError()
  44. serviceError.message = "服务器开小差,请稍后重试"
  45. serviceError.error_code = "parse_error"
  46. throw serviceError
  47. }
  48. }
  49. func mapArray<T: BaseMappable>(type: T.Type) throws -> [T]{
  50. let text = String(bytes: self.data, encoding: .utf8)
  51. if self.statusCode < 400 {
  52. return Mapper<T>().mapArray(JSONString: text!)!
  53. }
  54. do{
  55. let serviceError = Mapper<ServiceError>().map(JSONString: text!)
  56. throw serviceError!
  57. }catch{
  58. if error is ServiceError {
  59. throw error
  60. }
  61. let serviceError = ServiceError()
  62. serviceError.message = "服务器开小差,请稍后重试"
  63. serviceError.error_code = "parse_error"
  64. throw serviceError
  65. }
  66. }
  67. }
  68. class ServiceError:Error,Mappable{
  69. var message:String = ""
  70. var error_code:String = ""
  71. required init?(map: Map) {}
  72. init() {
  73. }
  74. func mapping(map: Map) {
  75. error_code <- map["error_code"]
  76. message <- map["error"]
  77. }
  78. var localizedDescription: String{
  79. return message
  80. }
  81. }
创建 User 类
  1. // User.swift
  2. import ObjectMapper
  3. class User: Mappable {
  4. required init?(map: Map) {}
  5. func mapping(map: Map) {
  6. userId <- map["userId"]
  7. name <- map["name"]
  8. age <- map["age"]
  9. }
  10. var userId:Int = 0
  11. var name:String = ""
  12. var age:Int = 0
  13. }
测试
  1. do{
  2. let user = try provider.rx.request(.user(userId: "101")).mapObject(type: User.self).toBlocking().first()
  3. print("user.name = \(user?.name)")
  4. }catch{
  5. print(error)
  6. }
  7. do{
  8. let user = try provider.rx.request(.user(userId: "101")).asObservable().mapObject(type: User.self).toBlocking().first()
  9. print("user.name = \(user?.name)")
  10. }catch{
  11. print(error)
  12. }
  13. do{
  14. let users = try provider.rx.request(.userQuery(keyword: "Wiki")).mapArray(type: User.self).toBlocking().first()
  15. print("test8 users.count = \(users?.count)")
  16. }catch{
  17. if error is ServiceError {
  18. print((error as! ServiceError).message)
  19. }
  20. print(error)
  21. }

打印日志

  1. private func JSONResponseDataFormatter(_ data: Data) -> Data {
  2. do {
  3. let dataAsJSON = try JSONSerialization.jsonObject(with: data)
  4. let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
  5. return prettyData
  6. } catch {
  7. return data // fallback to original data if it can't be serialized.
  8. }
  9. }
  1. let provider = MoyaProvider<MyApiService>(plugins: [NetworkLoggerPlugin(verbose: true, responseDataFormatter: JSONResponseDataFormatter)])

基于Moya、RxSwift和ObjectMapper优雅实现REST API请求的更多相关文章

  1. Swift高仿iOS网易云音乐Moya+RxSwift+Kingfisher+MVC+MVVM

    效果 列文章目录 因为目录比较多,每次更新这里比较麻烦,所以推荐点击到主页,然后查看iOS Swift云音乐专栏. 目简介 这是一个使用Swift(还有OC版本)语言,从0开发一个iOS平台,接近企业 ...

  2. laravel5.7 前后端分离开发 实现基于API请求的token认证

    最近在学习前后端分离开发,发现 在laravel中实现前后台分离是无法无法使用 CSRF Token 认证的.因为 web 请求的用户认证是通过Session和客户端Cookie的实现的,而前后端分离 ...

  3. 【Go】优雅的读取http请求或响应的数据-续

    原文链接:https://blog.thinkeridea.com/201902/go/you_ya_de_du_qu_http_qing_qiu_huo_xiang_ying_de_shu_ju_2 ...

  4. 【Go】优雅的读取http请求或响应的数据

    [Go]优雅的读取http请求或响应的数据 原文链接:https://blog.thinkeridea.com/201901/go/you_ya_de_du_qu_http_qing_qiu_huo_ ...

  5. 使用axios优雅的发起网络请求

    原文链接:https://www.jianshu.com/p/73585303fdc0 公司项目使用了vue作为技术栈,便理所应当地使用了官方推荐的axios进行网络请求,这里记录下axios的封装方 ...

  6. 基于.Net平台C#的微信网页版API

    git上有很多类似的项目,但大多都是python和js的,为了便于.Net windows平台的使用,我重构了一个.Net版本的,已整理开源 https://github.com/leestar54/ ...

  7. react封装基于axios的API请求

    一.最近做的一个后台管理项目,基于antd-pro做的,需要封装基于axios请求,便于开发,直接上代码. import axios from 'axios'; export const Method ...

  8. Azure Load Balancer(二) 基于内部的负载均衡来转发为访问请求

    一,引言 上一节,我们使用 Azure Load Balancer 类型为外部的,来转发我们的 Web 服务.今天我们看看另一种类型为 “Internal” 的 Azure Load Balancer ...

  9. Moya/RxSwift/ObjectMapper/Alamofire开发

    废话不多说直接上代码 // // MoyaNetWorking.swift // GreenAir // // Created by BruceAlbert on 2017/9/18. // Copy ...

随机推荐

  1. luogu P1938 [USACO09NOV]找工就业Job Hunt

    题目描述 奶牛们正在找工作.农场主约翰知道后,鼓励奶牛们四处碰碰运气.而且他还加了一条要求:一头牛在一个城市最多只能赚D(1≤D≤1000)美元,然后它必须到另一座城市工作.当然,它可以在别处工作一阵 ...

  2. IDEA插件开发(一)一个简单的表单demo

  3. JSSDK制作思路

    需求:对外提供一个js的SDK.相当于在原有的原生SDK基础上包装一层方法. SDK原生的方法通过JSExport 协议可以让js调用到原生的方法.你可以写一个协议继承JSExport ,将需要对js ...

  4. POJ 1325 Machine schedine (二分图-最小点覆盖数=最大匹配边数)

    As we all know, machine scheduling is a very classical problem in computer science and has been stud ...

  5. HashMap 实现原理解析

    概要 HashMap 最早出现在 JDK 1.2 中,底层基于散列算法实现.HashMap 允许 null 键和 null 值,在计算哈键的哈希值时,null 键哈希值为 0.HashMap 并不保证 ...

  6. Jquery判断当前时PC端,移动端,平板端屏幕

    $(function(){     // console.log(navigator.userAgent);     var os = function (){       var ua = navi ...

  7. Spring Cloud Alibaba 新一代微服务解决方案

    本篇是「跟我学 Spring Cloud Alibaba」系列的第一篇, 每期文章会在公众号「架构进化论」进行首发更新,欢迎关注. 1.Spring Cloud Alibaba 是什么 Spring ...

  8. python之encode和decode编码

    u = '中文' str3 = u.encode('utf-8') # 以utf-8编码对u进行编码,获得bytes类型对象 print(str3) u2 = str3.decode('utf-8') ...

  9. python 正则表达式re使用模块(match()、search()和compile())

    摘录 python核心编程 python的re模块允许多线程共享一个已编译的正则表达式对象,也支持命名子组.下表是常见的正则表达式属性: 函数/方法 描述 仅仅是re模块函数 compile(patt ...

  10. 《CSAPP》实验一:位操作

    <CSAPP>号称程序员圣经,虽然中文译名为<深入理解计算机系统>,但其实没那么"深",只是覆盖面很广,一般用作计算机专业大一导论课的教科书.早就听闻书上配 ...