基于Moya、RxSwift和ObjectMapper优雅实现REST API请求
在Android开发中有非常强大的 Retrofit 请求,结合RxJava可以非常方便实现 RESTful API 网络请求。在 iOS开发中也有非常强大的网络请求库 Moya ,Moya是一个基于 Alamofire 开发的,轻量级的Swift网络层。Moya的可扩展性非常强,可以方便和RXSwift、ObjectMapper结合。
测试 REST API 定义
我们先用服务端定义几个REST API,开发者根据自己的条件来实现。
请求错误格式实例
{ "error": "密码错误", "error_code": "password_error" }
测试 API 列表
- http://127.0.0.1:8080/account/login,参数username、password,post请求,成功响应为User。
- http://127.0.0.1:8080/user/{userId},get请求,成功响应为User。
- 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)])
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
深入理解Java线程状态
赞助平台 首页 / 文章管理 / 文章编辑 Java线程状态友情提示:文章每30秒自动保存一次,编辑器支持图片拖动上传或者复制粘贴上传~ 0 线程状态概述 分类 6个状态定义: java.lang.Thread.State New: 尚未启动的线程的线程状态。 Runnable: 可运行线程的线程状态,等待CPU调度。 Blocked: 线程阻塞等待监视器锁定的线程状态。处于synchronized同步代码块或方法中被阻塞。 Waiting: 等待线程的线程状态。下 列不带超时的方式:Object.wait、Thread.join、 LockSupport.park Timed Waiting:具有指定等待时间的等待线程的线程状态。下 列带超时的方式:Thread.sleep、0bject.wait、 Thread.join、 LockSuppor
- 下一篇
进击的 Java ,云原生时代的蜕变
作者| 易立 阿里云资深技术专家导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的。然而,本文的作者却认为云原生时代,Java 依然可以胜任“巨人”的角色。作者希望通过一系列实验,开拓同学视野,提供有益思考。 在企业软件领域,Java 依然是绝对王者,但它让开发者既爱又恨。一方面因为其丰富的生态和完善的工具支持,可以极大提升了应用开发效率;但在运行时效率方面,Java 也背负着”内存吞噬者“,“CPU 撕裂者“的恶名,持续受到 NodeJS、Python、Golang 等新老语言的挑战。 在技术社区,我们经常看到有人在唱衰 Java 技术,认为其不再符合云原生计算发展的趋势。我们先抛开这些观点,首先思考一下云原生对应用运行时的不同需求。 体积更小 - 对于微服务分布式架构而言,更小的体
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境