三葫盼、將結(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)用豆瓣FM
的API
接口败匹,獲取所有的頻道列表并顯示在表格中。 - 點擊任意一個頻道讥巡,調(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) ObjectMapper
的 Mappable
協(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)用 Moya
的 Provider
進(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)
}
}