您现在的位置是:首页 > 文章详情

基于Moya、RxSwift和ObjectMapper优雅实现REST API请求

日期:2019-09-16点击:400

在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)])
原文链接:https://yq.aliyun.com/articles/718489
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章