Swift - RxSwift的使用詳解50(結(jié)合Moya使用2:結(jié)果處理楷怒、模型轉(zhuǎn)換)

三葫盼、將結(jié)果轉(zhuǎn)為 JSON 對象

1残腌,實現(xiàn)方法

(1)如果服務(wù)器返回的數(shù)據(jù)是 json 格式的話,直接通過 Moya 提供的 mapJSON 方法即可將其轉(zhuǎn)成 JSON 對象贫导。

注意:關(guān)于 DouBanProvider 里的具體內(nèi)容抛猫,可以參考上文(點擊查看)。

//獲取數(shù)據(jù)
DouBanProvider.rx.request(.channels)
    .subscribe(onSuccess: { response in
        //數(shù)據(jù)處理
        let json = try? response.mapJSON() as! [String: Any]
        print("--- 請求成功孩灯!返回的如下數(shù)據(jù) ---")
        print(json!)
    },onError: { error in
        print("數(shù)據(jù)請求失敗!錯誤原因:", error)
         
    }).disposed(by: disposeBag)

(2)或者使用下面這種寫法也是可以的闺金。

//獲取數(shù)據(jù)
DouBanProvider.rx.request(.channels)
    .mapJSON()
    .subscribe(onSuccess: { data in
        //數(shù)據(jù)處理
        let json = data as! [String: Any]
        print("--- 請求成功!返回的如下數(shù)據(jù) ---")
        print(json)
    },onError: { error in
        print("數(shù)據(jù)請求失敗!錯誤原因:", error)
         
    }).disposed(by: disposeBag)

(3)運行結(jié)果如下:

2峰档,使用樣例

(1)效果圖

  • 我們使用 Moya 調(diào)用豆瓣 FMAPI 接口败匹,獲取所有的頻道列表并顯示在表格中。
  • 點擊任意一個頻道讥巡,調(diào)用另一個接口隨機(jī)獲取該頻道下的一首歌曲掀亩,并彈出顯示。

(2)樣例代碼

import UIKit
import RxSwift
import RxCocoa
 
class ViewController: UIViewController {
     
    //顯示頻道列表的tableView
    var tableView:UITableView!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //創(chuàng)建表視圖
        self.tableView = UITableView(frame:self.view.frame, style:.plain)
        //創(chuàng)建一個重用的單元格
        self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        self.view.addSubview(self.tableView!)
         
        //獲取列表數(shù)據(jù)
        let data = DouBanProvider.rx.request(.channels)
            .mapJSON()
            .map{ data -> [[String: Any]] in
                if let json = data as? [String: Any],
                    let channels = json["channels"] as? [[String: Any]] {
                    return channels
                }else{
                    return []
                }
            }.asObservable()
         
        //將數(shù)據(jù)綁定到表格
        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)
         
        //單元格點擊
        tableView.rx.modelSelected([String: Any].self)
            .map{ $0["channel_id"] as! String }
            .flatMap{ DouBanProvider.rx.request(.playlist($0)) }
            .mapJSON()
            .subscribe(onNext: {[weak self] data in
                //解析數(shù)據(jù)欢顷,獲取歌曲信息
                if let json = data as? [String: Any],
                    let musics = json["song"] as? [[String: Any]]{
                    let artist = musics[0]["artist"]!
                    let title = musics[0]["title"]!
                    let message = "歌手:\(artist)\n歌曲:\(title)"
                    //將歌曲信息彈出顯示
                    self?.showAlert(title: "歌曲信息", message: message)
                }               
            }).disposed(by: disposeBag)
    }
     
    //顯示消息
    func showAlert(title:String, message:String){
        let alertController = UIAlertController(title: title,
                                                message: message, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        self.present(alertController, animated: true, completion: nil)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

四归榕,將結(jié)果映射成自定義對象

1,準(zhǔn)備工作

(1)要實現(xiàn)數(shù)據(jù)轉(zhuǎn)模型(model)吱涉,我們這里還要先引入一個第三方的數(shù)據(jù)模型轉(zhuǎn)換框架:ObjectMapper。關(guān)于它的安裝配置外里,以及相關(guān)說明可以參考航哥之前寫的文章:

(2)為了讓 ObjectMapper 能夠更好地與 Moya 配合使用怎爵,我們需要使用 Moya-ObjectMapper 這個 Observable 擴(kuò)展庫。它的作用是增加數(shù)據(jù)轉(zhuǎn)模型對象盅蝗、以及數(shù)據(jù)轉(zhuǎn)模型對象數(shù)組這兩個方法鳖链。我們現(xiàn)將其下載到本地。

(3)Moya-ObjectMapper 配置很簡單只需把 sourcs 文件夾中的如下 3 個文件添加到項目中來即可墩莫。

  • Response+ObjectMapper.swift
  • ObservableType+ObjectMapper.swift
  • Single+ObjectMapper.swift

2芙委,使用樣例

(1)我們還是以前面的豆瓣音樂頻道數(shù)據(jù)為例。首先我定義好相關(guān)模型(需要實現(xiàn) ObjectMapperMappable 協(xié)議狂秦,并設(shè)置好成員對象與 JSON 屬性的相互映射關(guān)系灌侣。)

//豆瓣接口模型
struct Douban: Mappable {
    //頻道列表
    var channels: [Channel]?
     
    init?(map: Map) { }
     
    // Mappable
    mutating func mapping(map: Map) {
        channels <- map["channels"]
    }
}
 
//頻道模型
struct Channel: Mappable {
    var name: String?
    var nameEn:String?
    var channelId: String?
    var seqId: Int?
    var abbrEn: String?
     
    init?(map: Map) { }
     
    // Mappable
    mutating func mapping(map: Map) {
        name <- map["name"]
        nameEn <- map["name_en"]
        channelId <- map["channel_id"]
        seqId <- map["seq_id"]
        abbrEn <- map["abbr_en"]
    }
}
 
//歌曲列表模型
struct Playlist: Mappable {
    var r: Int!
    var isShowQuickStart: Int!
    var song:[Song]!
     
    init?(map: Map) { }
     
    // Mappable
    mutating func mapping(map: Map) {
        r <- map["r"]
        isShowQuickStart <- map["is_show_quick_start"]
        song <- map["song"]
    }
}
 
//歌曲模型
struct Song: Mappable {
    var title: String!
    var artist: String!
     
    init?(map: Map) { }
     
    // Mappable
    mutating func mapping(map: Map) {
        title <- map["title"]
        artist <- map["artist"]
    }
}

(2)下面樣例演示如何獲取數(shù)據(jù),并轉(zhuǎn)換成對應(yīng)的模型裂问。

//獲取數(shù)據(jù)
DouBanProvider.rx.request(.channels)
    .mapObject(Douban.self)
    .subscribe(onSuccess: { douban in
        if let channels = douban.channels {
            print("--- 共\(channels.count)個頻道 ---")
            for channel in channels {
                if let name = channel.name, let channelId = channel.channelId {
                    print("\(name) (id:\(channelId))")
                }
            }
        }
    }, onError: { error in
        print("數(shù)據(jù)請求失敗!錯誤原因:", error)
    })
    .disposed(by: disposeBag)

(3)下面樣例演示將數(shù)據(jù)換成模型侧啼,并綁定到表格上顯示牛柒。

import UIKit
import RxSwift
import RxCocoa
import ObjectMapper
 
class ViewController: UIViewController {
     
    //顯示頻道列表的tableView
    var tableView:UITableView!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //創(chuàng)建表視圖
        self.tableView = UITableView(frame:self.view.frame, style:.plain)
        //創(chuàng)建一個重用的單元格
        self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        self.view.addSubview(self.tableView!)
         
        //獲取列表數(shù)據(jù)
        let data = DouBanProvider.rx.request(.channels)
            .mapObject(Douban.self)
            .map{ $0.channels ?? [] }
            .asObservable()
         
        //將數(shù)據(jù)綁定到表格
        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)
         
        //單元格點擊
        tableView.rx.modelSelected(Channel.self)
            .map{ $0.channelId! }
            .flatMap{ DouBanProvider.rx.request(.playlist($0)) }
            .mapObject(Playlist.self)
            .subscribe(onNext: {[weak self] playlist in
                //解析數(shù)據(jù),獲取歌曲信息
                if playlist.song.count > 0 {
                    let artist = playlist.song[0].artist!
                    let title = playlist.song[0].title!
                    let message = "歌手:\(artist)\n歌曲:\(title)"
                    //將歌曲信息彈出顯示
                    self?.showAlert(title: "歌曲信息", message: message)
                }
            }).disposed(by: disposeBag)
    }
     
    //顯示消息
    func showAlert(title:String, message:String){
        let alertController = UIAlertController(title: title,
                                                message: message, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        self.present(alertController, animated: true, completion: nil)
    }
}

功能改進(jìn):將網(wǎng)絡(luò)請求服務(wù)提取出來

(1)上面的樣例中我們是在 VC 里是直接調(diào)用 MoyaProvider 進(jìn)行數(shù)據(jù)請求痊乾,并進(jìn)行模型轉(zhuǎn)換皮壁。

(2)我們也可以把網(wǎng)絡(luò)請求和數(shù)據(jù)轉(zhuǎn)換相關(guān)代碼提取出來,作為一個專門的 Service哪审。比如 DouBanNetworkService蛾魄,內(nèi)容如下:

import RxSwift
import RxCocoa
import ObjectMapper
 
class DouBanNetworkService {
     
    //獲取頻道數(shù)據(jù)
    func loadChannels() -> Observable<[Channel]> {
        return DouBanProvider.rx.request(.channels)
            .mapObject(Douban.self)
            .map{ $0.channels ?? [] }
            .asObservable()
    }
     
    //獲取歌曲列表數(shù)據(jù)
    func loadPlaylist(channelId:String) -> Observable<Playlist> {
        return DouBanProvider.rx.request(.playlist(channelId))
            .mapObject(Playlist.self)
            .asObservable()
    }
     
    //獲取頻道下第一首歌曲
    func loadFirstSong(channelId:String) -> Observable<Song> {
        return loadPlaylist(channelId: channelId)
            .filter{ $0.song.count > 0}
            .map{ $0.song[0] }
    }
}

(3)VC 這邊不再直接調(diào)用 provider,而是通過這個 Service 就獲取需要的數(shù)據(jù)湿滓〉涡耄可以看到代碼簡潔許多:

import UIKit
import RxSwift
import RxCocoa
import ObjectMapper
 
class ViewController: UIViewController {
     
    //顯示頻道列表的tableView
    var tableView:UITableView!
     
    let disposeBag = DisposeBag()
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //創(chuàng)建表視圖
        self.tableView = UITableView(frame:self.view.frame, style:.plain)
        //創(chuàng)建一個重用的單元格
        self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        self.view.addSubview(self.tableView!)
         
        //豆瓣網(wǎng)絡(luò)請求服務(wù)
        let networkService = DouBanNetworkService()
         
        //獲取列表數(shù)據(jù)
        let data = networkService.loadChannels()
         
        //將數(shù)據(jù)綁定到表格
        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)
         
        //單元格點擊
        tableView.rx.modelSelected(Channel.self)
            .map{ $0.channelId! }
            .flatMap(networkService.loadFirstSong)
            .subscribe(onNext: {[weak self] song in
                //將歌曲信息彈出顯示
                let message = "歌手:\(song.artist!)\n歌曲:\(song.title!)"
                self?.showAlert(title: "歌曲信息", message: message)
            }).disposed(by: disposeBag)
    }
     
    //顯示消息
    func showAlert(title:String, message:String){
        let alertController = UIAlertController(title: title,
                                                message: message, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        self.present(alertController, animated: true, completion: nil)
    }
}

RxSwift使用詳解系列
原文出自:www.hangge.com轉(zhuǎn)載請保留原文鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市茉稠,隨后出現(xiàn)的幾起案子描馅,更是在濱河造成了極大的恐慌,老刑警劉巖而线,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铭污,死亡現(xiàn)場離奇詭異,居然都是意外死亡膀篮,警方通過查閱死者的電腦和手機(jī)掉伏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砰蠢,“玉大人婆咸,你說我怎么就攤上這事】曷牛” “怎么了涧偷?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毙死。 經(jīng)常有香客問我燎潮,道長,這世上最難降的妖魔是什么扼倘? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任确封,我火速辦了婚禮,結(jié)果婚禮上再菊,老公的妹妹穿的比我還像新娘爪喘。我一直安慰自己,他們只是感情好纠拔,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布秉剑。 她就那樣靜靜地躺著,像睡著了一般稠诲。 火紅的嫁衣襯著肌膚如雪秃症。 梳的紋絲不亂的頭發(fā)上候址,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音种柑,去河邊找鬼岗仑。 笑死,一個胖子當(dāng)著我的面吹牛聚请,可吹牛的內(nèi)容都是我干的荠雕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼驶赏,長吁一口氣:“原來是場噩夢啊……” “哼炸卑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起煤傍,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盖文,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚯姆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體五续,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年龄恋,在試婚紗的時候發(fā)現(xiàn)自己被綠了疙驾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡郭毕,死狀恐怖它碎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情显押,我是刑警寧澤扳肛,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站乘碑,受9級特大地震影響挖息,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝉仇,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殖蚕。 院中可真熱鬧轿衔,春花似錦、人聲如沸睦疫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛤育。三九已至宛官,卻和暖如春葫松,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背底洗。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工腋么, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亥揖。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓珊擂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親费变。 傳聞我的和親對象是個殘疾皇子摧扇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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