在Android開發(fā)中有非常強(qiáng)大的 Retrofit 請(qǐng)求命贴,結(jié)合RxJava可以非常方便實(shí)現(xiàn) RESTful API 網(wǎng)絡(luò)請(qǐng)求。在 iOS開發(fā)中也有非常強(qiáng)大的網(wǎng)絡(luò)請(qǐng)求庫(kù) Moya 萧豆,Moya是一個(gè)基于 Alamofire 開發(fā)的,輕量級(jí)的Swift網(wǎng)絡(luò)層。Moya的可擴(kuò)展性非常強(qiáng),可以方便和RXSwift昏名、ObjectMapper結(jié)合涮雷。
測(cè)試 REST API 定義
我們先用服務(wù)端定義幾個(gè)REST API,開發(fā)者根據(jù)自己的條件來(lái)實(shí)現(xiàn)葡粒。
請(qǐng)求錯(cuò)誤格式實(shí)例
{
"error": "密碼錯(cuò)誤",
"error_code": "password_error"
}
測(cè)試 API 列表
- http://127.0.0.1:8080/account/login份殿,參數(shù)username、password嗽交,post請(qǐng)求卿嘲,成功響應(yīng)為User。
- http://127.0.0.1:8080/user/{userId}夫壁,get請(qǐng)求拾枣,成功響應(yīng)為User。
- http://127.0.0.1:8080/user/query?q={keyword}盒让,get請(qǐng)求梅肤,成功響應(yīng)為User列表。
創(chuàng)建接口
// MyApiService.swift
import Moya
enum MyApiService {
case login(username:String,password:String)
case user(userId:String)
case userQuery(keyword:String)
}
extension MyApiService:TargetType{
// 定義請(qǐng)求的host
var baseURL: URL {
return URL(string: "http://127.0.0.1:8080")!
}
// 定義請(qǐng)求的路徑
var path: String {
switch self {
case .login(_, _):
return "/account/login"
case .user(let userId):
return "user/\(userId)"
case .userQuery(_):
return "user/query"
}
}
// 定義接口請(qǐng)求方式
var method: Moya.Method {
switch self {
case .login:
return .post
case .user,.userQuery:
return .get
}
}
// 定義模擬數(shù)據(jù)
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)!
}
}
// 構(gòu)建參數(shù)
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)
}
}
// 構(gòu)建請(qǐng)求頭部
var headers: [String : String]? {
return ["Content-type": "application/json"]
}
}
請(qǐng)求數(shù)據(jù)
let provider = MoyaProvider<MyApiService>()
// Moya 提供最原始的請(qǐng)求方式邑茄,響應(yīng)的數(shù)據(jù)是二進(jìn)制
provider.request(.user(userId: "101")){ result in
// do something with the result
let text = String(bytes: result.value!.data, encoding: .utf8)
print("text1 = \(text)")
}
// 結(jié)合RxSwift姨蝴,響應(yīng)的數(shù)據(jù)是二進(jìn)制
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把數(shù)據(jù)轉(zhuǎn)換成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把數(shù)據(jù)轉(zhuǎn)換成json格式,并轉(zhuǎn)換成最常見的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
})
請(qǐng)求數(shù)據(jù):RxBlocking
RxBlocking使用教程 肺缕,可以使用同步的方式請(qǐng)求網(wǎng)絡(luò)
import RxBlocking
do{
let text = try provider.rx.request(.user(userId: "101")).mapJSON().toBlocking().first()
print("text5 = \(text)")
}catch{
print(error)
}
結(jié)合 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 = "服務(wù)器開小差左医,請(qǐng)稍后重試"
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 = "服務(wù)器開小差授帕,請(qǐng)稍后重試"
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
}
}
創(chuàng)建 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
}
測(cè)試
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)])