当前位置: 移动技术网 > IT编程>移动开发>IOS > 基于Moya、RxSwift和ObjectMapper优雅实现REST API请求

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

2019年09月18日  | 移动技术网IT编程  | 我要评论

uedbet8.co,3.19政变,最帅男嘉宾

在android开发中有非常强大的 retrofit 请求,结合rxjava可以非常方便实现 restful api 网络请求。在 ios开发中也有非常强大的网络请求库 moya ,moya是一个基于 alamofire 开发的,轻量级的swift网络层。moya的可扩展性非常强,可以方便和rxswift、objectmapper结合。

测试 rest api 定义

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

请求错误格式实例
{
    "error": "密码错误",
    "error_code": "password_error"
}
测试 api 列表

创建接口

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网