一、请求网络数据
① 通过 rx.response 请求数据
如下所示,通过豆瓣提供的音乐频道列表接口获取数据,并将返回结果输出到控制台中:
"https://www.douban.com/j/app/radio/channels" - i - v
Success ( 2134ms) : Status 200
返回的数据是: { "channels" : [ { "name_en" : "Personal Radio" , "seq_id" : 0 , "abbr_en" : "My" , "name" : "私人兆赫" , "channel_id" : 0 } , { "name" : "华语" , "seq_id" : 0 , "abbr_en" : "" , "channel_id" : "1" , "name_en" : "" } , { "name" : "欧美" , "seq_id" : 1 , "abbr_en" : "" , "channel_id" : "2" , "name_en" : "" } , { "name" : "七零" , "seq_id" : 2 , "abbr_en" : "" , "channel_id" : "3" , "name_en" : "" } , { "name" : "八零" , "seq_id" : 3 , "abbr_en" : "" , "channel_id" : "4" , "name_en" : "" } , { "name" : "九零" , "seq_id" : 4 , "abbr_en" : "" , "channel_id" : "5" , "name_en" : "" } , { "name" : "粤语" , "seq_id" : 5 , "abbr_en" : "" , "channel_id" : "6" , "name_en" : "" } , { "name" : "摇滚" , "seq_id" : 6 , "abbr_en" : "" , "channel_id" : "7" , "name_en" : "" } , { "name" : "民谣" , "seq_id" : 7 , "abbr_en" : "" , "channel_id" : "8" , "name_en" : "" } , { "name" : "轻音乐" , "seq_id" : 8 , "abbr_en" : "" , "channel_id" : "9" , "name_en" : "" } , { "name" : "原声" , "seq_id" : 9 , "abbr_en" : "" , "channel_id" : "10" , "name_en" : "" } , { "name" : "Fly by midnight " , "seq_id" : 10 , "abbr_en" : "" , "channel_id" : "267" , "name_en" : "" } , { "name" : "独立" , "seq_id" : 11 , "abbr_en" : "" , "channel_id" : "268" , "name_en" : "" } , { "name" : "爵士" , "seq_id" : 12 , "abbr_en" : "" , "channel_id" : "13" , "name_en" : "" } , { "name" : "电子" , "seq_id" : 13 , "abbr_en" : "" , "channel_id" : "14" , "name_en" : "" } , { "name" : "说唱" , "seq_id" : 14 , "abbr_en" : "" , "channel_id" : "15" , "name_en" : "" } , { "name" : "R&B " , "seq_id" : 15 , "abbr_en" : "" , "channel_id" : "16" , "name_en" : "" } , { "name" : "日语" , "seq_id" : 16 , "abbr_en" : "" , "channel_id" : "17" , "name_en" : "" } , { "name" : "韩语" , "seq_id" : 17 , "abbr_en" : "" , "channel_id" : "18" , "name_en" : "" } , { "name" : "我的巴比伦恋人" , "seq_id" : 18 , "abbr_en" : "" , "channel_id" : "259" , "name_en" : "" } , { "name" : "女声" , "seq_id" : 19 , "abbr_en" : "" , "channel_id" : "20" , "name_en" : "" } , { "name" : "法语" , "seq_id" : 20 , "abbr_en" : "" , "channel_id" : "22" , "name_en" : "" } , { "name" : "户外" , "seq_id" : 21 , "abbr_en" : "" , "channel_id" : "151" , "name_en" : "" } , { "name" : "休息" , "seq_id" : 22 , "abbr_en" : "" , "channel_id" : "152" , "name_en" : "" } , { "name" : "工作学习" , "seq_id" : 23 , "abbr_en" : "" , "channel_id" : "153" , "name_en" : "" } , { "name" : "亢奋" , "seq_id" : 24 , "abbr_en" : "" , "channel_id" : "154" , "name_en" : "" } , { "name" : "古典" , "seq_id" : 25 , "abbr_en" : "" , "channel_id" : "27" , "name_en" : "" } , { "name" : "动漫" , "seq_id" : 26 , "abbr_en" : "" , "channel_id" : "28" , "name_en" : "" } , { "name" : "咖啡馆" , "seq_id" : 27 , "abbr_en" : "" , "channel_id" : "32" , "name_en" : "" } , { "name" : "舒缓" , "seq_id" : 28 , "abbr_en" : "" , "channel_id" : "155" , "name_en" : "" } , { "name" : "18岁青春的召唤" , "seq_id" : 29 , "abbr_en" : "" , "channel_id" : "262" , "name_en" : "" } , { "name" : "红歌" , "seq_id" : 30 , "abbr_en" : "" , "channel_id" : "41" , "name_en" : "" } , { "name" : "圣诞" , "seq_id" : 31 , "abbr_en" : "" , "channel_id" : "170" , "name_en" : "" } , { "name" : "运动" , "seq_id" : 32 , "abbr_en" : "" , "channel_id" : "257" , "name_en" : "" } , { "name" : "英语" , "seq_id" : 33 , "abbr_en" : "" , "channel_id" : "264" , "name_en" : "" } , { "name" : "豆瓣好歌曲" , "seq_id" : 34 , "abbr_en" : "" , "channel_id" : "179" , "name_en" : "" } , { "name" : "Future Pop" , "seq_id" : 35 , "abbr_en" : "" , "channel_id" : "266" , "name_en" : "" } , { "name" : "金属" , "seq_id" : 36 , "abbr_en" : "" , "channel_id" : "187" , "name_en" : "" } , { "name" : "布鲁斯" , "seq_id" : 37 , "abbr_en" : "" , "channel_id" : "188" , "name_en" : "" } , { "name" : "新歌" , "seq_id" : 38 , "abbr_en" : "" , "channel_id" : "61" , "name_en" : "" } , { "name" : "世界杯" , "seq_id" : 39 , "abbr_en" : "" , "channel_id" : "201" , "name_en" : "" } , { "name" : "朋克" , "seq_id" : 40 , "abbr_en" : "" , "channel_id" : "76" , "name_en" : "" } , { "name" : "Easy " , "seq_id" : 41 , "abbr_en" : "" , "channel_id" : "77" , "name_en" : "" } , { "name" : "91.1 " , "seq_id" : 42 , "abbr_en" : "" , "channel_id" : "78" , "name_en" : "" } , { "name" : "乡村" , "seq_id" : 43 , "abbr_en" : "" , "channel_id" : "269" , "name_en" : "" } , { "name" : "“砖”属音乐" , "seq_id" : 44 , "abbr_en" : "" , "channel_id" : "145" , "name_en" : "" } , { "name" : "Pop" , "seq_id" : 45 , "abbr_en" : "" , "channel_id" : "194" , "name_en" : "" } , { "name" : "拉丁" , "seq_id" : 46 , "abbr_en" : "" , "channel_id" : "189" , "name_en" : "" } ] }
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. response ( request: request) . subscribe ( onNext: {
( response, data) in
let str = String ( data: data, encoding: String . Encoding . utf8)
print ( "返回的数据是:" , str ? ? "" )
} ) . disposed ( by: disposeBag)
从以上示例,可以看到,不管请求成功与否都会进入到 onNext 这个回调中,如果需要根据响应状态进行一些相应操作,比如:
如果是异常状态码(比如:404)则弹出告警提示框。 可以借助 response 参数进行判断即可,把 url 改成一个错误的地址:
let urlString = "https://www.douban.com/xxxxxxxxxx/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. response ( request: request) . subscribe ( onNext: {
( response, data) in
if 200 . . < 300 ~ = response. statusCode {
let str = String ( data: data, encoding: String . Encoding . utf8)
print ( "请求成功!返回的数据是:" , str ? ? "" )
} else {
print ( "请求失败!" )
}
} ) . disposed ( by: disposeBag)
curl - X GET
"https://www.douban.com/xxxxxxxxxx/app/radio/channels" - i - v
Failure ( 1448ms) : Status 404
请求失败!
② 通过 rx.data 请求数据
rx.data 与 rx.response 的区别:
如果不需要获取底层的 response,只需知道请求是否成功,以及成功时返回的结果,那么建议使用 rx.data。
因为 rx.data 会自动对响应状态码进行判断,只有成功的响应(状态码为 200~300)才会进入到 onNext 这个回调,否则进入 onError 这个回调。 如果不需要考虑请求失败的情况,只对成功返回的结果做处理可以在 onNext 回调中进行相关操作:
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. data ( request: request) . subscribe ( onNext: {
data in
let str = String ( data: data, encoding: String . Encoding . utf8)
print ( "请求成功!返回的数据是:" , str ? ? "" )
} ) . disposed ( by: disposeBag)
"https://www.douban.com/j/app/radio/channels" - i - v
Success ( 1449ms) : Status 200
请求成功!返回的数据是: { "channels" : [ { "name_en" : "Personal Radio" , "seq_id" : 0 , "abbr_en" : "My" , "name" : "私人兆赫" , "channel_id" : 0 } , { "name" : "华语" , "seq_id" : 0 , "abbr_en" : "" , "channel_id" : "1" , "name_en" : "" } , { "name" : "欧美" , "seq_id" : 1 , "abbr_en" : "" , "channel_id" : "2" , "name_en" : "" } , { "name" : "七零" , "seq_id" : 2 , "abbr_en" : "" , "channel_id" : "3" , "name_en" : "" } , { "name" : "八零" , "seq_id" : 3 , "abbr_en" : "" , "channel_id" : "4" , "name_en" : "" } , { "name" : "九零" , "seq_id" : 4 , "abbr_en" : "" , "channel_id" : "5" , "name_en" : "" } , { "name" : "粤语" , "seq_id" : 5 , "abbr_en" : "" , "channel_id" : "6" , "name_en" : "" } , { "name" : "摇滚" , "seq_id" : 6 , "abbr_en" : "" , "channel_id" : "7" , "name_en" : "" } , { "name" : "民谣" , "seq_id" : 7 , "abbr_en" : "" , "channel_id" : "8" , "name_en" : "" } , { "name" : "轻音乐" , "seq_id" : 8 , "abbr_en" : "" , "channel_id" : "9" , "name_en" : "" } , { "name" : "原声" , "seq_id" : 9 , "abbr_en" : "" , "channel_id" : "10" , "name_en" : "" } , { "name" : "Fly by midnight " , "seq_id" : 10 , "abbr_en" : "" , "channel_id" : "267" , "name_en" : "" } , { "name" : "独立" , "seq_id" : 11 , "abbr_en" : "" , "channel_id" : "268" , "name_en" : "" } , { "name" : "爵士" , "seq_id" : 12 , "abbr_en" : "" , "channel_id" : "13" , "name_en" : "" } , { "name" : "电子" , "seq_id" : 13 , "abbr_en" : "" , "channel_id" : "14" , "name_en" : "" } , { "name" : "说唱" , "seq_id" : 14 , "abbr_en" : "" , "channel_id" : "15" , "name_en" : "" } , { "name" : "R&B " , "seq_id" : 15 , "abbr_en" : "" , "channel_id" : "16" , "name_en" : "" } , { "name" : "日语" , "seq_id" : 16 , "abbr_en" : "" , "channel_id" : "17" , "name_en" : "" } , { "name" : "韩语" , "seq_id" : 17 , "abbr_en" : "" , "channel_id" : "18" , "name_en" : "" } , { "name" : "我的巴比伦恋人" , "seq_id" : 18 , "abbr_en" : "" , "channel_id" : "259" , "name_en" : "" } , { "name" : "女声" , "seq_id" : 19 , "abbr_en" : "" , "channel_id" : "20" , "name_en" : "" } , { "name" : "法语" , "seq_id" : 20 , "abbr_en" : "" , "channel_id" : "22" , "name_en" : "" } , { "name" : "户外" , "seq_id" : 21 , "abbr_en" : "" , "channel_id" : "151" , "name_en" : "" } , { "name" : "休息" , "seq_id" : 22 , "abbr_en" : "" , "channel_id" : "152" , "name_en" : "" } , { "name" : "工作学习" , "seq_id" : 23 , "abbr_en" : "" , "channel_id" : "153" , "name_en" : "" } , { "name" : "亢奋" , "seq_id" : 24 , "abbr_en" : "" , "channel_id" : "154" , "name_en" : "" } , { "name" : "古典" , "seq_id" : 25 , "abbr_en" : "" , "channel_id" : "27" , "name_en" : "" } , { "name" : "动漫" , "seq_id" : 26 , "abbr_en" : "" , "channel_id" : "28" , "name_en" : "" } , { "name" : "咖啡馆" , "seq_id" : 27 , "abbr_en" : "" , "channel_id" : "32" , "name_en" : "" } , { "name" : "舒缓" , "seq_id" : 28 , "abbr_en" : "" , "channel_id" : "155" , "name_en" : "" } , { "name" : "18岁青春的召唤" , "seq_id" : 29 , "abbr_en" : "" , "channel_id" : "262" , "name_en" : "" } , { "name" : "红歌" , "seq_id" : 30 , "abbr_en" : "" , "channel_id" : "41" , "name_en" : "" } , { "name" : "圣诞" , "seq_id" : 31 , "abbr_en" : "" , "channel_id" : "170" , "name_en" : "" } , { "name" : "运动" , "seq_id" : 32 , "abbr_en" : "" , "channel_id" : "257" , "name_en" : "" } , { "name" : "英语" , "seq_id" : 33 , "abbr_en" : "" , "channel_id" : "264" , "name_en" : "" } , { "name" : "豆瓣好歌曲" , "seq_id" : 34 , "abbr_en" : "" , "channel_id" : "179" , "name_en" : "" } , { "name" : "Future Pop" , "seq_id" : 35 , "abbr_en" : "" , "channel_id" : "266" , "name_en" : "" } , { "name" : "金属" , "seq_id" : 36 , "abbr_en" : "" , "channel_id" : "187" , "name_en" : "" } , { "name" : "布鲁斯" , "seq_id" : 37 , "abbr_en" : "" , "channel_id" : "188" , "name_en" : "" } , { "name" : "新歌" , "seq_id" : 38 , "abbr_en" : "" , "channel_id" : "61" , "name_en" : "" } , { "name" : "世界杯" , "seq_id" : 39 , "abbr_en" : "" , "channel_id" : "201" , "name_en" : "" } , { "name" : "朋克" , "seq_id" : 40 , "abbr_en" : "" , "channel_id" : "76" , "name_en" : "" } , { "name" : "Easy " , "seq_id" : 41 , "abbr_en" : "" , "channel_id" : "77" , "name_en" : "" } , { "name" : "91.1 " , "seq_id" : 42 , "abbr_en" : "" , "channel_id" : "78" , "name_en" : "" } , { "name" : "乡村" , "seq_id" : 43 , "abbr_en" : "" , "channel_id" : "269" , "name_en" : "" } , { "name" : "“砖”属音乐" , "seq_id" : 44 , "abbr_en" : "" , "channel_id" : "145" , "name_en" : "" } , { "name" : "Pop" , "seq_id" : 45 , "abbr_en" : "" , "channel_id" : "194" , "name_en" : "" } , { "name" : "拉丁" , "seq_id" : 46 , "abbr_en" : "" , "channel_id" : "189" , "name_en" : "" } ] }
如果还要处理失败的情况,可以在 onError 回调中操作:
let urlString = "https://www.douban.com/xxxxxx/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. data ( request: request) . subscribe ( onNext: {
data in
let str = String ( data: data, encoding: String . Encoding . utf8)
print ( "请求成功!返回的数据是:" , str ? ? "" )
} , onError: { error in
print ( "请求失败!错误原因:" , error)
} ) . disposed ( by: disposeBag)
"https://www.douban.com/xxxxxx/app/radio/channels" - i - v
Failure ( 7189ms) : Status 404
请求失败!错误原因: HTTP request failed with `404 `.
二、手动发起请求和取消请求
在很多情况下,网络请求并不是由程序自动发起的,可能需要我们点击个按钮,或者切换个标签时才去请求数据。除了手动发起请求外,同样的可能还需要手动取消上一次的网络请求(如果未完成),那么 RxSwift 该如何实现呢? 如下所示:
如果请求没返回时,点击“取消请求”则可将其取消(取消后即使返回数据也不作处理)。
@IBOutlet weak var startBtn: UIButton !
@IBOutlet weak var cancelBtn: UIButton !
let disposeBag = DisposeBag ( )
override func viewDidLoad ( ) {
super . viewDidLoad ( )
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
startBtn. rx. tap. asObservable ( )
. flatMap {
URLSession . shared. rx. data ( request: request)
. takeUntil ( self . cancelBtn. rx. tap)
}
. subscribe ( onNext: {
data in
let str = String ( data: data, encoding: String . Encoding . utf8)
print ( "请求成功!返回的数据是:" , str ? ? "" )
} , onError: { error in
print ( "请求失败!错误原因:" , error)
} ) . disposed ( by: disposeBag)
三、将结果转为 Json 对象
如果服务器返回的数据是 json 格式的话,可以使用 iOS 内置的 JSONSerialization 将其转成 JSON 对象,方便使用:
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. data ( request: request) . subscribe ( onNext: {
data in
let json = try ? ( JSONSerialization . jsonObject ( with: data, options: . allowFragments)
as ! [ String : Any ] )
print ( "--- 请求成功,返回的如下数据 ---" )
print ( json! )
} ) . disposed ( by: disposeBag)
"https://www.douban.com/j/app/radio/channels" - i - v
Success ( 3410ms) : Status 200
-- - 请求成功!返回的如下数据 -- -
[ "channels" : < __NSArrayI 0x7ff0f2f07080 > (
{
"abbr_en" = "" ;
"channel_id" = 9 ;
name = "\U8f7b\U97f3\U4e50" ;
"name_en" = "" ;
"seq_id" = 8 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 10 ;
name = "\U539f\U58f0" ;
"name_en" = "" ;
"seq_id" = 9 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 267 ;
name = "Fly by midnight " ;
"name_en" = "" ;
"seq_id" = 10 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 268 ;
name = "\U72ec\U7acb" ;
"name_en" = "" ;
"seq_id" = 11 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 13 ;
name = "\U7235\U58eb" ;
"name_en" = "" ;
"seq_id" = 12 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 14 ;
name = "\U7535\U5b50" ;
"name_en" = "" ;
"seq_id" = 13 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 15 ;
name = "\U8bf4\U5531" ;
"name_en" = "" ;
"seq_id" = 14 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 16 ;
name = "R&B " ;
"name_en" = "" ;
"seq_id" = 15 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 17 ;
name = "\U65e5\U8bed" ;
"name_en" = "" ;
"seq_id" = 16 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 18 ;
name = "\U97e9\U8bed" ;
"name_en" = "" ;
"seq_id" = 17 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 259 ;
name = "\U6211\U7684\U5df4\U6bd4\U4f26\U604b\U4eba" ;
"name_en" = "" ;
"seq_id" = 18 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 20 ;
name = "\U5973\U58f0" ;
"name_en" = "" ;
"seq_id" = 19 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 22 ;
name = "\U6cd5\U8bed" ;
"name_en" = "" ;
"seq_id" = 20 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 151 ;
name = "\U6237\U5916" ;
"name_en" = "" ;
"seq_id" = 21 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 152 ;
name = "\U4f11\U606f" ;
"name_en" = "" ;
"seq_id" = 22 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 28 ;
name = "\U52a8\U6f2b" ;
"name_en" = "" ;
"seq_id" = 26 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 32 ;
name = "\U5496\U5561\U9986" ;
"name_en" = "" ;
"seq_id" = 27 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 155 ;
name = "\U8212\U7f13" ;
"name_en" = "" ;
"seq_id" = 28 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 262 ;
name = "18\U5c81\U9752\U6625\U7684\U53ec\U5524" ;
"name_en" = "" ;
"seq_id" = 29 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 41 ;
name = "\U7ea2\U6b4c" ;
"name_en" = "" ;
"seq_id" = 30 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 170 ;
name = "\U5723\U8bde" ;
"name_en" = "" ;
"seq_id" = 31 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 257 ;
name = "\U8fd0\U52a8" ;
"name_en" = "" ;
"seq_id" = 32 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 264 ;
name = "\U82f1\U8bed" ;
"name_en" = "" ;
"seq_id" = 33 ;
} ,
{
"abbr_en" = "" ;
"channel_id" = 179 ;
name = "\U8c46\U74e3\U597d\U6b4c\U66f2" ;
"name_en" = "" ;
"seq_id" = 34 ;
}
)
]
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. data ( request: request)
. map {
try JSONSerialization . jsonObject ( with: $0 , options: . allowFragments)
as ! [ String : Any ]
}
. subscribe ( onNext: {
data in
print ( "--- 请求成功!返回的如下数据 ---" )
print ( data)
} ) . disposed ( by: disposeBag)
还有更简单的方法,就是直接使用 RxSwift 提供的 rx.json 方法去获取数据,它会直接将结果转成 Json 对象:
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. json ( request: request) . subscribe ( onNext: {
data in
let json = data as ! [ String : Any ]
print ( "--- 请求成功!返回的如下数据 ---" )
print ( json )
} ) . disposed ( by: disposeBag)
将获取到的豆瓣频道列表数据转换成 Json 对象,并绑定到表格上显示:
self . tableView = UITableView ( frame: self . view. frame, style: . plain)
self . tableView! . register ( UITableViewCell . self , forCellReuseIdentifier: "Cell" )
self . view. addSubview ( self . tableView! )
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
let data = URLSession . shared. rx. json ( request: request)
. map { result - > [ [ String : Any ] ] in
if let data = result as ? [ String : Any ] ,
let channels = data[ "channels" ] as ? [ [ String : Any ] ] {
return channels
} else {
return [ ]
}
}
data. bind ( to: tableView. rx. items) { ( tableView, row, element) in
let cell = tableView. dequeueReusableCell ( withIdentifier: "Cell" ) !
cell. textLabel? . text = "\( row) :\( element[ "name" ] ! ) "
return cell
} . disposed ( by: disposeBag)
四、将结果映射成自定义对象
① 准备工作
要实现数据到模型(model)的转换,首先需要引入一个第三方的数据模型转换框架:ObjectMapper。 为了让 ObjectMapper 能够更好地与 RxSwift 配合使用,对 Observable 进行扩展(RxObjectMapper.swift),增加数据转模型对象、以及数据转模型对象数组这两个方法:
import ObjectMapper
import RxSwift
public enum RxObjectMapperError : Error {
case parsingError
}
public extension Observable where Element : Any {
public func mapObject< T> ( type: T. Type ) - > Observable < T> where T: Mappable {
let mapper = Mapper < T> ( )
return self . map { ( element) - > T in
guard let parsedElement = mapper. map ( JSONObject : element) else {
throw RxObjectMapperError . parsingError
}
return parsedElement
}
}
public func mapArray< T> ( type: T. Type ) - > Observable < [ T] > where T: Mappable {
let mapper = Mapper < T> ( )
return self . map { ( element) - > [ T] in
guard let parsedArray = mapper. mapArray ( JSONObject : element) else {
throw RxObjectMapperError . parsingError
}
return parsedArray
}
}
}
② 使用示例
以豆瓣音乐频道数据为例,首先定义好相关模型(需要实现 ObjectMapper 的 Mappable 协议,并设置好成员对象与 Json 属性的相互映射关系):
class Douban : Mappable {
var channels: [ Channel ] ?
init ( ) {
}
required init ? ( map : Map ) {
}
func mapping ( map : Map ) {
channels < - map [ "channels" ]
}
}
class Channel : Mappable {
var name: String ?
var nameEn: String ?
var channelId: String ?
var seqId: Int ?
var abbrEn: String ?
init ( ) {
}
required init ? ( map : Map ) {
}
func mapping ( map : Map ) {
name < - map [ "name" ]
nameEn < - map [ "name_en" ]
channelId < - map [ "channel_id" ]
seqId < - map [ "seq_id" ]
abbrEn < - map [ "abbr_en" ]
}
}
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
URLSession . shared. rx. json ( request: request)
. mapObject ( type: Douban . self )
. subscribe ( onNext: { ( douban: 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) )" )
}
}
}
} ) . disposed ( by: disposeBag)
self . tableView = UITableView ( frame: self . view. frame, style: . plain)
self . tableView! . register ( UITableViewCell . self , forCellReuseIdentifier: "Cell" )
self . view. addSubview ( self . tableView! )
let urlString = "https://www.douban.com/j/app/radio/channels"
let url = URL ( string: urlString)
let request = URLRequest ( url: url! )
let data = URLSession . shared. rx. json ( request: request)
. mapObject ( type: Douban . self )
. map { $0 . channels ? ? [ ] }
data. bind ( to: tableView. rx. items) { ( tableView, row, element) in
let cell = tableView. dequeueReusableCell ( withIdentifier: "Cell" ) !
cell. textLabel? . text = "\( row) :\( element. name! ) "
return cell
} . disposed ( by: disposeBag)