使用此三劍客,訪問網(wǎng)絡(luò)更加優(yōu)雅荞彼。
Moya
Moya基于Alamofire的更高層網(wǎng)絡(luò)請求封裝抽象層.使用Moya訪問網(wǎng)絡(luò)有以下優(yōu)點:
1.編譯時檢查正確的API端點訪問.
2.使你定義不同端點枚舉值對應相應的用途更加明晰.
3.提高測試地位從而使單元測試更加容易.
ObjectMapper
ObjectMapper是一個基于Swift語言開發(fā)的能夠讓 JSON 與 Object 之間輕易轉(zhuǎn)換的類庫帜乞。通過ObjectMapper我們可以將 JSON 數(shù)據(jù)轉(zhuǎn)換成 Model 對象或?qū)?Model 對象轉(zhuǎn)換成 JSON 數(shù)據(jù)库说。
RxSwift
RxSwift是Swift函數(shù)響應式編程的一個開源庫,由Github的ReactiveX組織開發(fā)割疾,維護馋艺。
RxSwift的目的是讓讓數(shù)據(jù)/事件流和異步任務能夠更方便的序列化處理栅干,能夠使用swift進行響應式編程。
在這里我強烈建議學習下RxSwift丈钙,尤其在使用MVVM模式下非驮,可以實現(xiàn)雙向綁定,下面推薦一些學習RxSwift的學習資源雏赦。
小菁的視頻
CocoaPods
使用Moya + ObjectMapper + RxSwift最好使用CocoaPods進行集成,可以方便的管理項目里面的第三方庫芙扎。關(guān)于如何使用CocoaPods星岗,請google。
使用CocoaPods將下列第三方庫進行集成戒洼。
platform :ios,'8.0'
use_frameworks!
target'YzzSwift'do
inherit! :search_paths
inhibit_all_warnings!
pod'RxSwift'
pod'RxCocoa'
pod'Moya'
pod'Moya/RxSwift'
pod'ObjectMapper'
ObjectMapper
首先建立一個AccountModel.swift
俏橘,導入ObjectMapper
,實現(xiàn)Mappable
import ObjectMapper
struct AccountModel: Mappable {
var accountID:String!
var avatarIcon:String?
var nickName:String?
var tk:String!
var userType:String!
init?(map: Map) {
}
mutating func mapping(map: Map) {
accountID <- map["accountID"]
avatarIcon <- map["avatarIcon"]
nickName <- map["nickName"]
tk <- map["tk"]
userType <- map["userType"]
}
}
在類中是無需標注mutating
關(guān)鍵字的圈浇,mutating
只針對值類型寥掐,如枚舉靴寂,結(jié)構(gòu)體 )
RequestManger
新建RequestManger.swift
import Alamofire
import Moya
class RequestManger {
class CustomServerTrustPoliceManager : ServerTrustPolicyManager {
override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
return .disableEvaluation
}
public init() {
super.init(policies: [:])
}
}
class func defaultAlamofireManager() -> Manager {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let manager = Alamofire.SessionManager(configuration: configuration,serverTrustPolicyManager: CustomServerTrustPoliceManager())
manager.startRequestsImmediately = false
return manager
}
class func endpointMapping<Target: TargetType>(target: Target) -> Endpoint<Target> {
return MoyaProvider.defaultEndpointMapping(for: target)
}
}
注意:默認是Alamfire
,我這里是自己定制召耘。主要是處理關(guān)于ssl的問題百炬,具體可以點擊關(guān)于對manger
如何添加受信任的白名單。EndpointClosure
可以對請求參數(shù)做進一步的修改,如可以修改endpointByAddingParameters endpointByAddingHTTPHeaderFields等
RequestPlugin
Moya中默認提供的3個插件
- NetworkLoggerPlugin
- NetworkActivityPlugin
- HTTP Authentication
import Moya
import Result
import SVProgressHUD
class RequestPlugin: PluginType {
let tk = "jKle07aY0pbSaP8ACk8_ZktK-0ivmFUTIDoLnDBw_FOSCw=="
let accountID = "1000024"
let tz = "Asia/Shanghai"
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
var request = request
request.addValue(tk, forHTTPHeaderField: "tk")
request.addValue(accountID, forHTTPHeaderField: "accountID")
request.addValue(tz, forHTTPHeaderField: "tz")
return request
}
func willSend(_ request: RequestType, target: TargetType) {
print("*****************************************************************************\n請求連接:\(target.baseURL)\(target.path) \n方法:\(target.method)\n參數(shù):\(String(describing: target.parameters)) ")
SVProgressHUD.show()
}
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
SVProgressHUD.dismiss()
let result = result
switch result {
case .failure(let moyaError):
switch moyaError {
case .underlying(let error) :
let errorNs = error as NSError
print(errorNs.code)
break
default:
break
}
break
default:
break
}
}
func process(_ result: Result<Response, MoyaError>, target: TargetType) -> Result<Response, MoyaError> {
return result
}
}
注意:這里可以在prepare
中添加request
請求頭污它,比如一些網(wǎng)絡(luò)訪問的token
.
同時可以在此插件中添加網(wǎng)絡(luò)訪問指示器剖踊,willSend
中啟動指示器,didReceive
中關(guān)閉指示器衫贬,process
方法主要是用于對訪問網(wǎng)絡(luò)的結(jié)果進行修改再返回德澈。另外關(guān)于MoyaError
的錯誤我們該如何解析,需要使用到switch
來先解析MoyaError
固惯,然后再解析underlying
梆造,將解析出來的Error
轉(zhuǎn)換成NSError
,最后輸入錯誤結(jié)果。
NetworkLoggerPlugin
可以用來進行日志的管理葬毫,NetworkActivityPlugin
可以在此插件中處理狀態(tài)欄中的小菊花.
TargetType
新建VesyncApi.swift
import Moya
import Alamofire
enum VesyncApi{
case login(account: String, password: String)
case deviceList()
}
extension VesyncApi:TargetType {
var baseURL: URL {
return URL(string: "https://192.168.100.60:5005")!
}
var path: String {
switch self {
case .login(_, _):
return "/vold/user/login"
case .deviceList():
return "/vold/user/devices"
}
}
var method: Moya.Method {
switch self {
case .login(_, _):
return .post
case .deviceList:
return .get
}
}
var parameters: [String : Any]? {
switch self {
case let .login(account, password):
return ["account": account, "password": password]
default :
return nil
}
}
public var parameterEncoding: ParameterEncoding {
return JSONEncoding.default
}
var sampleData: Data {
// switch self {
// case .login(_, _):
return "".data(using: String.Encoding.utf8)!
// }
}
var task: Task {
// switch self {
// case .login(_, _):
return .request
// }
}
}
RxSwift和ObjectMapper的中間件
新建Observable+ObjectMapper.swift
import Foundation
import RxSwift
import Moya
import ObjectMapper
import Result
extension Observable {
func mapObject<T: Mappable>(type: T.Type) -> Observable<T> {
return self.map { response in
//if response is a dictionary, then use ObjectMapper to map the dictionary
//if not throw an error
guard let dict = response as? [String: Any] else {
throw RxSwiftMoyaError.ParseJSONError
}
if let error = self.parseError(response: dict) {
throw error
}
return Mapper<T>().map(JSON: dict)!
}
}
func mapArray<T: Mappable>(type: T.Type) -> Observable<[T]> {
return self.map { response in
guard let dict = response as? [[String: Any]] else {
throw RxSwiftMoyaError.ParseJSONError
}
if let error = self.parseError(response: dict) {
throw error
}
return Mapper<T>().mapArray(JSONArray: dicts)
}
}
func parseServerError() -> Observable {
return self.map { (response) in
let name = type(of: response)
print(name)
guard let dict = response as? [String: Any] else {
throw RxSwiftMoyaError.ParseJSONError
}
if let error = self.parseError(response: dict) {
throw error
}
return self as! Element
}
}
fileprivate func parseError(response: [String: Any]?) -> NSError? {
var error: NSError?
if let value = response {
var code:Int?
var msg:String?
if let errorDic = value["error"] as? [String:Any]{
code = errorDic["code"] as? Int
msg = errorDic["msg"] as? String
error = NSError(domain: "Network", code: code!, userInfo: [NSLocalizedDescriptionKey: msg ?? ""])
}
}
return error
}
}
enum RxSwiftMoyaError: String {
case ParseJSONError
case OtherError
}
extension RxSwiftMoyaError: Swift.Error {}
provider
訪問網(wǎng)絡(luò)前我們需要先實例化一個provider
let provider = RxMoyaProvider<VesyncApi>(endpointClosure: RequestManger.endpointMapping,manager:RequestManger.defaultAlamofireManager(),plugins:[RequestPlugin()])
RxSwift
和ObjectMapper
配合使用訪問
provider.request(.login(account: account, password: password))
.mapJSON().mapObject(type: AccountModel.self).subscribe(onNext: { (model) in
print(model.accountID)
}, onError: { (error) in
let e = error as NSError
print(e.code)
}).addDisposableTo(disposeBag)
注意:如果是object
我們則使用mapObject
镇辉,Array
則使用mapArray
參考文章:
http://www.reibang.com/p/a728d03db236
最后希望大家對此文章做出指點,好改正不足供常。謝謝