生活随笔
收集整理的這篇文章主要介紹了
RxSwift之深入解析URLSession的数据请求和数据处理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、請求網絡數據
① 通過 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
) inlet 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
) inif 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
inlet 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
inlet str
= String(data
: data
, encoding
: String.Encoding.utf8
)print("請求成功!返回的數據是:", str
?? "")
}, onError
: { error
inprint("請求失敗!錯誤原因:", 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
inlet str
= String(data
: data
, encoding
: String.Encoding.utf8
)print("請求成功!返回的數據是:", str
?? "")}, onError
: { error
inprint("請求失敗!錯誤原因:", 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
inlet 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
inprint("--- 請求成功!返回的如下數據 ---")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
inlet 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]] inif 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
) inlet 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
inguard 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
] inguard 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) inif 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
) inlet cell
= tableView
.dequeueReusableCell(withIdentifier
: "Cell")!cell
.textLabel
?.text
= "\(row):\(element.name!)"return cell
}.disposed(by
: disposeBag
)
總結
以上是生活随笔為你收集整理的RxSwift之深入解析URLSession的数据请求和数据处理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。