在Android開發中有很是強大的 Retrofit 請求,結合RxJava能夠很是方便實現 RESTful API 網絡請求。在 iOS開發中也有很是強大的網絡請求庫 Moya ,Moya是一個基於 Alamofire 開發的,輕量級的Swift網絡層。Moya的可擴展性很是強,能夠方便和RXSwift、ObjectMapper結合。git
咱們先用服務端定義幾個REST API,開發者根據本身的條件來實現。github
{ "error": "密碼錯誤", "error_code": "password_error" }
// 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使用教程 ,可使用同步的方式請求網絡json
import RxBlocking do{ let text = try provider.rx.request(.user(userId: "101")).mapJSON().toBlocking().first() print("text5 = \(text)") }catch{ print(error) }
pod 'ObjectMapper', '~> 3.4'
// 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.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)])