在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. set容器

    set容器简介: 1) set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列.元素插入过程是按排序规则插入,所以不能指定插入位置. 2) set采用红黑树变体的数据结构实现,红 ...

  2. k8s 开船记-触礁:四涡轮发动机撞坏3个引发502故障

    (图片来自网络) 非常抱歉,这次开船触礁故障给您带来麻烦了,请您谅解. 在我们昨天发布 k8s 开船记首航博文后,有园友在评论中发来贺词——“泰坦尼克号出发了[狗头]”,借此吉言,今天船就触礁了,还好 ...

  3. goal

    线段树进阶,主席树,莫队,相似kmp,悬线,单调栈,单调队列,ac自动机,后缀数组,后缀自动机 图论:最短路,差分约束,二分图,网络流,连通分量 不学图论了我太难了   三分找,单调增+max+单调减 ...

  4. POJ 3660 cow contest (Folyed 求传递闭包)

    N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we ...

  5. 笔记||Python3之布尔表达式+条件判断

    布尔表达式:        布尔类型:特性:只有两种情况 ---   真  /  假 1 -- True    False 2 -- type(True) ------------ <class ...

  6. 笔记||Python3之列表与元组

    列表List: 特性:①列表也是一种Squence类型 ②下标 ③能切片 ④可以存储任何类型的数据,每个元素是任意类型 ⑤内容可以改变:增删改查 1  --  值   列表的元素值是可以改变的   a ...

  7. NAT(地址解析协议)

    第七部分,也是本次更新的最后一部分,NAT(Network Address Translation),即地址解析协议.通俗理解,地址解析协议就是当一个单位只拥有一个公网ip地址,当内网中的主机想要访问 ...

  8. 解决oracle11g数据库监听连接不上问题

    java连接数据库报错12514,无法识别监听,但是PL客户端可以连接 oracle 监听 添加ip 同时修改tnsnames.ora.listener.ora将这两个文件中HOST后面的主机都修改为 ...

  9. centos7—计划任务(at、cron)

    centos7—计划任务(at.cron) 2018-08-08 14:33:17 coisini_覔 阅读数 3751更多 分类专栏: Linux基础 crond/at   版权声明:本文为博主原创 ...

  10. C语言每日一练——第3题

    一.题目要求 程序功能:计算100以内满足以下条件的所有整数i的个数cnt以及这些i之和sum.条件:i, i+4 ,i+10都是素数,同时i+10小于100.最后电影函数writeDAT()函数把结 ...