经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
基于Moya、RxSwift和ObjectMapper优雅实现REST API请求
来源:cnblogs  作者:ImWiki  时间:2019/9/18 8:48:12  对本文有异议

在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)])

原文链接:http://www.cnblogs.com/taoweiji/p/11538178.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号