RXSwift基礎網(wǎng)絡請求封裝

一、創(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
        }
    }
}

}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逞力,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子糠爬,更是在濱河造成了極大的恐慌寇荧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件执隧,死亡現(xiàn)場離奇詭異揩抡,居然都是意外死亡户侥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門峦嗤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕊唐,“玉大人,你說我怎么就攤上這事烁设√胬妫” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵装黑,是天一觀的道長副瀑。 經(jīng)常有香客問我,道長恋谭,這世上最難降的妖魔是什么糠睡? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮疚颊,結果婚禮上狈孔,老公的妹妹穿的比我還像新娘。我一直安慰自己串稀,他們只是感情好除抛,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著母截,像睡著了一般到忽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上清寇,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天喘漏,我揣著相機與錄音,去河邊找鬼华烟。 笑死翩迈,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的盔夜。 我是一名探鬼主播负饲,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喂链!你這毒婦竟也來了返十?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤椭微,失蹤者是張志新(化名)和其女友劉穎洞坑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝇率,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡迟杂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年刽沾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排拷。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡侧漓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出攻泼,到底是詐尸還是另有隱情火架,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布忙菠,位于F島的核電站何鸡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏牛欢。R本人自食惡果不足惜骡男,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望傍睹。 院中可真熱鬧隔盛,春花似錦、人聲如沸拾稳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽访得。三九已至龙亲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悍抑,已是汗流浹背鳄炉。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搜骡,地道東北人拂盯。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像记靡,于是被迫代替她去往敵國和親谈竿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1摸吠、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,980評論 3 119
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫空凸、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,101評論 4 62
  • 談自然療法似乎是件很抽象的事贩幻,然而對于2015年帶著“做自然療法信差”使命而誕生的平衡族健康科技公司而言轿腺,這是一件...
    平衡族閱讀 323評論 0 0
  • 有時候會莫名的有些小情緒两嘴,以前一個人的時候喜歡忍者,所以在別人眼中我一直都是一個很堅強很倔強的小姑娘族壳,因為憔辫。我...
    會飛的龍貓貓閱讀 200評論 0 0
  • 今日事件 1.服務簽約產(chǎn)業(yè)空間站站長組場邀約 2.確定一個產(chǎn)業(yè)空間站一個廠家,明日簽約 3.學習寧靜遠導師的產(chǎn)業(yè)生...
    全息心空間閱讀 137評論 0 0