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

测试 REST API 定义

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

请求错误格式实例
{
"error": "密码错误",
"error_code": "password_error"
}
测试 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列表。

创建接口

// MyApiService.swift
import Moya enum MyApiService {
case login(username:String,password:String)
case user(userId:String)
case userQuery(keyword:String)
}
extension MyApiService:TargetType{
// 定义请求的host
var baseURL: URL {
return URL(string: "http://127.0.0.1:8080")!
}
// 定义请求的路径
var path: String {
switch self {
case .login(_, _):
return "/account/login"
case .user(let userId):
return "user/\(userId)"
case .userQuery(_):
return "user/query"
}
}
// 定义接口请求方式
var method: Moya.Method {
switch self {
case .login:
return .post
case .user,.userQuery:
return .get
}
}
// 定义模拟数据
var sampleData: Data {
switch self {
case .login(let username, _):
return "{\"username\": \"\(username)\", \"id\": 100}".data(using: String.Encoding.utf8)!
case .user(_):
return "{\"username\": \"Wiki\", \"id\": 100}".data(using: String.Encoding.utf8)!
case .userQuery(_):
return "{\"username\": \"Wiki\", \"id\": 100}".data(using: String.Encoding.utf8)!
}
}
// 构建参数
var task: Task {
switch self {
case .login(let username, let passowrd):
return .requestParameters(parameters: ["username": username,"passowrd": passowrd], encoding: URLEncoding.default)
case .user(_):
return .requestPlain
case .userQuery(let keyword):
return .requestParameters(parameters: ["keyword": keyword], encoding: URLEncoding.default)
}
}
// 构建请求头部
var headers: [String : String]? {
return ["Content-type": "application/json"]
}
}

请求数据

let provider = MoyaProvider<MyApiService>()

// Moya 提供最原始的请求方式,响应的数据是二进制
provider.request(.user(userId: "101")){ result in
// do something with the result
let text = String(bytes: result.value!.data, encoding: .utf8)
print("text1 = \(text)")
} // 结合RxSwift,响应的数据是二进制
provider.rx.request(.user(userId: "101")).subscribe({result in
// do something with the result
switch result {
case let .success(response):
let text = String(bytes: response.data, encoding: .utf8)
print("text2 = \(text)")
case let .error(error):
print(error)
}
}) // 通过mapJSON把数据转换成json格式
provider.rx.request(.user(userId: "101")).mapJSON().subscribe({result in
// do something with the result
switch result {
case let .success(text):
print("text3 = \(text)")
case let .error(error):
print(error)
}
})
// 通过mapJSON把数据转换成json格式,并转换成最常见的Observable
provider.rx.request(.user(userId: "101")).mapJSON().asObservable().subscribe(onNext: { result in
// do something with the result
print("text4 = \(result)")
}, onError:{ error in
// do something with the error
})
请求数据:RxBlocking

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

import RxBlocking

do{
let text = try provider.rx.request(.user(userId: "101")).mapJSON().toBlocking().first()
print("text5 = \(text)")
}catch{
print(error)
}

结合 ObjectMapper

引入ObjectMapper
pod 'ObjectMapper', '~> 3.4'
编写RxSwift拓展代码
//  MoyaRxSwiftObjectMapperExtension.swift

import Foundation
import RxSwift
import Moya
import ObjectMapper public extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
func mapObject<T: BaseMappable>(type: T.Type) -> Single<T> {
return self.map{ response in
return try response.mapObject(type: type)
}
}
func mapArray<T: BaseMappable>(type: T.Type) -> Single<[T]> {
return self.map{ response in
return try response.mapArray(type: type)
}
}
}
public extension ObservableType where E == Response {
func mapObject<T: BaseMappable>(type: T.Type) -> Observable<T> {
return self.map{ response in
return try response.mapObject(type: type)
}
}
func mapArray<T: BaseMappable>(type: T.Type) -> Observable<[T]> {
return self.map{ response in
return try response.mapArray(type: type)
}
}
} public extension Response{
func mapObject<T: BaseMappable>(type: T.Type) throws -> T{
let text = String(bytes: self.data, encoding: .utf8)
if self.statusCode < 400 {
return Mapper<T>().map(JSONString: text!)!
}
do{
let serviceError = Mapper<ServiceError>().map(JSONString: text!)
throw serviceError!
}catch{
if error is ServiceError {
throw error
}
let serviceError = ServiceError()
serviceError.message = "服务器开小差,请稍后重试"
serviceError.error_code = "parse_error"
throw serviceError
}
}
func mapArray<T: BaseMappable>(type: T.Type) throws -> [T]{
let text = String(bytes: self.data, encoding: .utf8)
if self.statusCode < 400 {
return Mapper<T>().mapArray(JSONString: text!)!
}
do{
let serviceError = Mapper<ServiceError>().map(JSONString: text!)
throw serviceError!
}catch{
if error is ServiceError {
throw error
}
let serviceError = ServiceError()
serviceError.message = "服务器开小差,请稍后重试"
serviceError.error_code = "parse_error"
throw serviceError
}
}
}
class ServiceError:Error,Mappable{
var message:String = ""
var error_code:String = ""
required init?(map: Map) {}
init() { }
func mapping(map: Map) {
error_code <- map["error_code"]
message <- map["error"]
}
var localizedDescription: String{
return message
}
}
创建 User 类
//  User.swift
import ObjectMapper
class User: Mappable {
required init?(map: Map) {} func mapping(map: Map) {
userId <- map["userId"]
name <- map["name"]
age <- map["age"]
} var userId:Int = 0
var name:String = ""
var age:Int = 0
}
测试
do{
let user = try provider.rx.request(.user(userId: "101")).mapObject(type: User.self).toBlocking().first()
print("user.name = \(user?.name)")
}catch{
print(error)
}
do{
let user = try provider.rx.request(.user(userId: "101")).asObservable().mapObject(type: User.self).toBlocking().first()
print("user.name = \(user?.name)")
}catch{
print(error)
} do{
let users = try provider.rx.request(.userQuery(keyword: "Wiki")).mapArray(type: User.self).toBlocking().first()
print("test8 users.count = \(users?.count)")
}catch{
if error is ServiceError {
print((error as! ServiceError).message)
}
print(error)
}

打印日志

private func JSONResponseDataFormatter(_ data: Data) -> Data {
do {
let dataAsJSON = try JSONSerialization.jsonObject(with: data)
let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
return prettyData
} catch {
return data // fallback to original data if it can't be serialized.
}
}
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 P1759 通天之潜水

    题目背景 直达通天路·小A历险记第三篇 题目描述 在猴王的帮助下,小A终于走出了这篇荒山,却发现一条波涛汹涌的河拦在了自己的面前.河面上并没有船,但好在小A有n个潜水工具.由于他还要背重重的背包,所以 ...

  2. Java修炼——四种方式解析XML_DOM4J

    四种方式解析XML:DOM     JDOM    DOM4J    SAX 注意: DOM4J使用是需要上传jar包的. 先写一个XML栗子: <?xml version="1.0& ...

  3. python 金融应用(一)期权定价公式的计算

    一.基于不付息的欧式期权看涨BSM公式 假定股票服从下列微分方程: 期权定价公式: 二.蒙特卡洛模拟 import numpy as np import math from time import t ...

  4. 最全的linux系统安装教程和排错方法

    第4章 linux信息和系统安装与连接    260 4.1 linux的发展历史    260 4.2 GPL协议,FSF协议    261 4.3 linux系统的安装    261 4.3.1 ...

  5. 【Python3网络爬虫开发实战】 分析Ajax爬取今日头条街拍美图

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:haoxuan10 本节中,我们以今日头条为例来尝试通过分析Ajax请求 ...

  6. 几个非常适合练手的python爬虫项目,总有一个能搞定!

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:Python玩家 注意:如果你平时学Python有问题找不到人解答,可以 ...

  7. 使用iCamera 测试AR0331 300w高分辨率摄像头小结

    使用iCamera 测试AR0331 300w高分辨率摄像头小结 先看下sensor特性 分辨率最高可达:2048*1536=300w像素 1080p帧率最高可达60fps 本次使用usb2,帧率14 ...

  8. vscode代码自动补全失效

    前段时间,朋友说自己的vscode突然出现了代码不能自动补全的问题(主要是js,其他语言也可以参考) 症状表现为,刚打开vscode有自动补全,过了一会,突然就没了,反反复复 解决过程也是相当坎坷了 ...

  9. django----聚合查询 分组 F与Q查询 字段 及其 参数

    目录 一.orm补充查询 聚合查询 1-1 分组查询 1-2 F与Q查询 1-3 二. 字段及其参数 常用字段 AutoField IntegerField CharField DateField D ...

  10. 数组知识总结(js)

    js数组知识注意点: 声明空数组时,和c语言中的不同 js c var arr=[ ] //合法,声明一个空数组,数组长度为0; int a[];//错误因为在c中声明一个数组不仅要指定类型还要指定数 ...