一、創(chuàng)建項目州疾,集成cocoapods文件
target 'TestAPI' do
use_frameworks!
pod 'Alamofire'
pod 'SwiftyJSON'
pod 'RxSwift', '~>4.1.1'
pod 'RxCocoa', '~>4.1.1'
pod 'RxDataSources'
pod 'Moya/RxSwift', '~> 11.0'
pod 'NSObject+Rx'
pod 'SVProgressHUD'
end
這里使用的是Moya裳食、SwiftyJSON哆姻、Alamofire進行網(wǎng)絡請求的封裝。
二车遂、配置網(wǎng)絡請求
//1封断、根據(jù)Moya要求,網(wǎng)絡請求需要創(chuàng)建一個Provider,MoyaProvider需要我們傳入一個實現(xiàn)TargetType協(xié)議的對象舶担,這個協(xié)議里面包含了請求的路由地址和具體路徑坡疼、請求方式等網(wǎng)絡請求相關基本信息
let ComSmaManProvider = MoyaProvider<ComSmaManApi>(plugins:[AuthPlugin()])
//路由地址
let KBaseURL = "https://www.douban.com"
public enum ComSmaManApi{
//基本路由地址get、post請求
case post(suffixUrl:String, params:[String:Any])
case get(suffixUrl:String, params:[String:Any])
//其它路由地址get衣陶、post請求
case otherRequst(baseUrl:String, type:OtherBaseURLRequst)
public enum OtherBaseURLRequst {
case post(suffixUrl:String, params:[String:Any])
case get(suffixUrl:String, params:[String:Any])
}
}
//實現(xiàn)TargetType協(xié)議柄瑰,配置基本信息
extension ComSmaManApi:TargetType{
//路由地址
public var baseURL: URL {
let result = self.getConfigure()
return URL(string: result.1)!
}
//具體地址
public var path: String {
let result = self.getConfigure()
return result.2
}
//請求方式get、post
public var method: Moya.Method {
let result = self.getConfigure()
return result.0
}
//單元測試所用
public var sampleData: Data {
return "{}".data(using: .utf8)!
}
//請求任務事件(這里附帶上參數(shù))
public var task: Task {
//request上傳剪况、upload上傳教沾、download下載
let result = self.getConfigure()
return .requestParameters(parameters: result.3, encoding: URLEncoding.default)
}
//請求頭
public var headers: [String : String]? {
return nil
}
private func getConfigure() -> (Moya.Method,String,String,[String:Any]) {
switch self {
case .get(suffixUrl: let suffixUrl, params: let params):
return (.get,KBaseURL,suffixUrl,params)
case .post(suffixUrl: let suffixUrl, params: let params):
return (.post,KBaseURL,suffixUrl,params)
case .otherRequst(baseUrl: let baseUrl, type: let type):
switch type{
case .get(suffixUrl: let suffixUrl, params: let params):
return (.get,baseUrl,suffixUrl,params)
case .post(suffixUrl: let suffixUrl, params: let params):
return (.post,baseUrl,suffixUrl,params)
}
}
}
}
//當需要在header里面添加請求token或者時間戳等信息時可以傳入這些配置
struct AuthPlugin: PluginType {
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
/*
var request = request
UserDefaults.standard.synchronize()
let accessToken = UserDefaults.standard.value(forKey: "accessToken") as? String
let timestamp: Int = Int.getTimeStamp()
request.addValue("\(timestamp)", forHTTPHeaderField: "timestamp")
if accessToken != nil {
request.addValue(accessToken!, forHTTPHeaderField: "accessToken")
}
print(request.allHTTPHeaderFields)
*/
return request
}
}
三、配合SwiftyJSONMapper實現(xiàn)Data轉JSON,JSON轉Model
1译断、先創(chuàng)建一個JSONMappable協(xié)議授翻,讓我們創(chuàng)建的model都遵守這個協(xié)議
Snip20180810_1.png
2、對Response和PrimitiveSequence擴展
Snip20180810_2.png
Response+SwiftyJSONMapper
文件
public extension Response {
/// Maps data received from the signal into an object which implements the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type:T.Type) throws -> T {
let jsonObject = try mapJSON()
guard let mappedObject = T(fromJson: JSON(jsonObject)) else {
throw MoyaError.jsonMapping(self)
}
return mappedObject
}
/// Maps data received from the signal into an array of objects which implement the ALSwiftyJSONAble protocol
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type:[T.Type]) throws -> [T] {
let jsonObject = try mapJSON()
let mappedArray = JSON(jsonObject)
let mappedObjectsArray = mappedArray.arrayValue.flatMap { T(fromJson: $0) }
return mappedObjectsArray
}
}
extension Response {
@available(*, unavailable, renamed: "map(to:)")
public func mapObject<T: JSONMappable>(type:T.Type) throws -> T {
return try map(to: type)
}
@available(*, unavailable, renamed: "map(to:)")
public func mapArray<T: JSONMappable>(type:T.Type) throws -> [T] {
return try map(to: [type])
}
}
PrimitiveSequence+SwiftyJSONMapper
文件
/// Extension for processing Responses into Mappable objects through ObjectMapper
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
/// Maps data received from the signal into an object which implements the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type: T.Type) -> Single<T> {
return flatMap { response -> Single<T> in
return Single.just(try response.map(to: type))
}
}
/// Maps data received from the signal into an array of objects which implement the ALSwiftyJSONAble protocol.
/// If the conversion fails, the signal errors.
public func map<T: JSONMappable>(to type: [T.Type]) -> Single<[T]> {
return flatMap { response -> Single<[T]> in
return Single.just(try response.map(to: type))
}
}
}
3孙咪、創(chuàng)建model
import SwiftyJSON
struct Douban: JSONMappable{
var channels :[Channel]?
init(fromJson json: JSON) {
channels = json["channels"].arrayValue.map({Channel(fromJson: $0)})
}
}
struct Channel: JSONMappable {
var name :String?
var nameEn :String?
var channelId :String?
var seqId :String?
var abbrEn :String?
init(fromJson json: JSON) {
name = json["name"].stringValue
nameEn = json["nameEn"].stringValue
channelId = json["channel_id"].stringValue
seqId = json["seqId"].stringValue
abbrEn = json["abbrEn"].stringValue
}
}
struct Playlist:JSONMappable {
var r :Int!
var isShowQuickStart: Int!
var song:[Song]!
init(fromJson json: JSON) {
r = json["r"].intValue
isShowQuickStart = json["is_show_quick_start"].intValue
song = json["song"].arrayValue.map({Song(fromJson: $0)})
}
}
struct Song:JSONMappable {
var title: String!
var artist: String!
init(fromJson json: JSON) {
title = json["title"].stringValue
artist = json["artist"].stringValue
}
}
4堪唐、在viewcontroller中利用ComSmaManProvider請求數(shù)據(jù)
let data = ComSmaManProvider.rx.request(.get(suffixUrl: "/j/app/radio/channels", params: [:]))
.map(to: Douban.self) //轉換成model
.map{$0.channels ?? []} //獲取數(shù)組channels作為tableview的數(shù)據(jù)源
.asObservable()
//將數(shù)據(jù)綁定到tableview上
data.bind(to: tableView.rx.items){ (tableView,row,element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = element.name
cell.accessoryType = .disclosureIndicator
return cell
}.disposed(by: disposeBag)
5、最終效果
Snip20180810_3.png
四翎蹈、在實際開發(fā)項目中羔杨,我們都與后臺約定一個數(shù)據(jù)返回成功的標識,比如status為1是成功杨蛋,0為失敗等等其他狀態(tài)。
創(chuàng)建ResponseMapper
文件理澎,內(nèi)容如下
let RESULT_CODE = "status"
let RESULT_DATA = "data"
enum XYRequestStatus:String {
case RequstSuccess = "200"
case RequstError
}
enum XYError : Error {
case noRepresentor
case notSuccessfulHTTP
case noData
case couldNotMakeObjectError
case bizError(resultCode: String?, resultMsg: String?)
}
extension Observable{
private func resultFromJSON<T:JSONMappable>(jsonData:JSON,classType:T.Type) -> T? {
return T(fromJson: jsonData)
}
func mapResponseToObj<T:JSONMappable>(type:T.Type) -> Observable<T?> {
return map{ representor in
guard let response = representor as? Moya.Response else{
throw XYError.noRepresentor
}
guard ((200...209) ~= response.statusCode) else{
throw XYError.notSuccessfulHTTP
}
let json = try? JSON.init(data: response.data)
if let code = json?[RESULT_CODE].string{
if code == XYRequestStatus.RequstSuccess.rawValue{
return self.resultFromJSON(jsonData: json![RESULT_DATA], classType: type)
} else {
throw XYError.bizError(resultCode: json?["code"].string, resultMsg: json?["msg"].string)
}
}else{
throw XYError.couldNotMakeObjectError
}
}
}
func mapResponseToObjArray<T:JSONMappable>(type:T.Type) -> Observable<[T]> {
return map{ representor in
guard let response = representor as? Moya.Response else{
throw XYError.noRepresentor
}
guard ((200...209) ~= response.statusCode) else{
throw XYError.notSuccessfulHTTP
}
let json = try? JSON.init(data: response.data)
if let code = json?[RESULT_CODE].string{
if code == XYRequestStatus.RequstSuccess.rawValue{
var objects = [T]()
let objectsArray = json?[RESULT_DATA].array
if let array = objectsArray{
for object in array{
if let obj = self.resultFromJSON(jsonData: object, classType: type){
objects.append(obj)
}
}
return objects
}else{
throw XYError.noData
}
}else{
throw XYError.bizError(resultCode: json?["code"].string, resultMsg: json?["msg"].string)
}
}else{
throw XYError.couldNotMakeObjectError
}
}
}
}