美文网首页swift
Swift - RxSwift的使用详解50(结合Moya使用2

Swift - RxSwift的使用详解50(结合Moya使用2

作者: 八级大狂风AM | 来源:发表于2018-04-25 14:38 被阅读1816次

    三、将结果转为 JSON 对象

    1,实现方法

    (1)如果服务器返回的数据是 json 格式的话,直接通过 Moya 提供的 mapJSON 方法即可将其转成 JSON 对象。

    注意:关于 DouBanProvider 里的具体内容,可以参考上文(点击查看)。

    //获取数据
    DouBanProvider.rx.request(.channels)
        .subscribe(onSuccess: { response in
            //数据处理
            let json = try? response.mapJSON() as! [String: Any]
            print("--- 请求成功!返回的如下数据 ---")
            print(json!)
        },onError: { error in
            print("数据请求失败!错误原因:", error)
             
        }).disposed(by: disposeBag)
    
    

    (2)或者使用下面这种写法也是可以的。

    //获取数据
    DouBanProvider.rx.request(.channels)
        .mapJSON()
        .subscribe(onSuccess: { data in
            //数据处理
            let json = data as! [String: Any]
            print("--- 请求成功!返回的如下数据 ---")
            print(json)
        },onError: { error in
            print("数据请求失败!错误原因:", error)
             
        }).disposed(by: disposeBag)
    

    (3)运行结果如下:

    2,使用样例

    (1)效果图

    • 我们使用 Moya 调用豆瓣 FMAPI 接口,获取所有的频道列表并显示在表格中。
    • 点击任意一个频道,调用另一个接口随机获取该频道下的一首歌曲,并弹出显示。

    (2)样例代码

    import UIKit
    import RxSwift
    import RxCocoa
     
    class ViewController: UIViewController {
         
        //显示频道列表的tableView
        var tableView:UITableView!
         
        let disposeBag = DisposeBag()
         
        override func viewDidLoad() {
            super.viewDidLoad()
             
            //创建表视图
            self.tableView = UITableView(frame:self.view.frame, style:.plain)
            //创建一个重用的单元格
            self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
            self.view.addSubview(self.tableView!)
             
            //获取列表数据
            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()
             
            //将数据绑定到表格
            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
                    //解析数据,获取歌曲信息
                    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()
        }
    }
    

    四,将结果映射成自定义对象

    1,准备工作

    (1)要实现数据转模型(model),我们这里还要先引入一个第三方的数据模型转换框架:ObjectMapper。关于它的安装配置,以及相关说明可以参考航哥之前写的文章:

    (2)为了让 ObjectMapper 能够更好地与 Moya 配合使用,我们需要使用 Moya-ObjectMapper 这个 Observable 扩展库。它的作用是增加数据转模型对象、以及数据转模型对象数组这两个方法。我们现将其下载到本地。

    (3)Moya-ObjectMapper 配置很简单只需把 sourcs 文件夹中的如下 3 个文件添加到项目中来即可。

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

    2,使用样例

    (1)我们还是以前面的豆瓣音乐频道数据为例。首先我定义好相关模型(需要实现 ObjectMapperMappable 协议,并设置好成员对象与 JSON 属性的相互映射关系。)

    //豆瓣接口模型
    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)下面样例演示如何获取数据,并转换成对应的模型。

    //获取数据
    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("数据请求失败!错误原因:", error)
        })
        .disposed(by: disposeBag)
    

    (3)下面样例演示将数据换成模型,并绑定到表格上显示。

    import UIKit
    import RxSwift
    import RxCocoa
    import ObjectMapper
     
    class ViewController: UIViewController {
         
        //显示频道列表的tableView
        var tableView:UITableView!
         
        let disposeBag = DisposeBag()
         
        override func viewDidLoad() {
            super.viewDidLoad()
             
            //创建表视图
            self.tableView = UITableView(frame:self.view.frame, style:.plain)
            //创建一个重用的单元格
            self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
            self.view.addSubview(self.tableView!)
             
            //获取列表数据
            let data = DouBanProvider.rx.request(.channels)
                .mapObject(Douban.self)
                .map{ $0.channels ?? [] }
                .asObservable()
             
            //将数据绑定到表格
            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
                    //解析数据,获取歌曲信息
                    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)
        }
    }
    

    功能改进:将网络请求服务提取出来

    (1)上面的样例中我们是在 VC 里是直接调用 MoyaProvider 进行数据请求,并进行模型转换。

    (2)我们也可以把网络请求和数据转换相关代码提取出来,作为一个专门的 Service。比如 DouBanNetworkService,内容如下:

    import RxSwift
    import RxCocoa
    import ObjectMapper
     
    class DouBanNetworkService {
         
        //获取频道数据
        func loadChannels() -> Observable<[Channel]> {
            return DouBanProvider.rx.request(.channels)
                .mapObject(Douban.self)
                .map{ $0.channels ?? [] }
                .asObservable()
        }
         
        //获取歌曲列表数据
        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 这边不再直接调用 provider,而是通过这个 Service 就获取需要的数据。可以看到代码简洁许多:

    import UIKit
    import RxSwift
    import RxCocoa
    import ObjectMapper
     
    class ViewController: UIViewController {
         
        //显示频道列表的tableView
        var tableView:UITableView!
         
        let disposeBag = DisposeBag()
         
        override func viewDidLoad() {
            super.viewDidLoad()
             
            //创建表视图
            self.tableView = UITableView(frame:self.view.frame, style:.plain)
            //创建一个重用的单元格
            self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
            self.view.addSubview(self.tableView!)
             
            //豆瓣网络请求服务
            let networkService = DouBanNetworkService()
             
            //获取列表数据
            let data = networkService.loadChannels()
             
            //将数据绑定到表格
            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转载请保留原文链接

    相关文章

      网友评论

        本文标题:Swift - RxSwift的使用详解50(结合Moya使用2

        本文链接:https://www.haomeiwen.com/subject/keielftx.html