RxSwift之雙向綁定實戰

什麼是雙向綁定?雙向綁定是View與Model之間的一種相互響應的關係。 git

下面👇以一個Demo實戰來體驗一下雙向綁定帶來的快感!!!github

Demo要實現的功能比較簡單:在搜索框中輸入搜索內容 -> 發起網絡請求獲取搜索結果 -> 在tableView中展現搜索結果。 PS:UI代碼請小夥伴隨意腦補,就不贅述啦json

一、建立ViewModel

在UI搭建好以後,須要建立一個ViewModel類,來響應UI的變化。swift

class BOViewModel: NSObject {

    // 響應SearchBar的輸入
    let searchTextOB = BehaviorSubject(value: "")
}
複製代碼

添加屬性 searchTextOB 來響應輸入框的輸入變化,同時爲了能在收到響應後,發起網絡請求,因此使用了 BehaviorSubject類型。畢竟,Subject 類型能夠是序列,也能夠是觀察者。api

二、綁定UI到Model

class TBViewController: UIViewController {

    @IBOutlet weak var searchBar: UISearchBar!
    
    @IBOutlet weak var tableView: UITableView!
    
    let viewModel = BOViewModel()
    
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // 實現 view -綁定-> model
        self.searchBar.rx.text.orEmpty
            .bind(to: self.viewModel.searchTextOB)
            .disposed(by: self.disposeBag)
    }
}
複製代碼

使用 bind 函數,完成輸入框綁定到Model。數組

三、建立懶加載搜索結果序列

lazy var searchData: Driver<[BOReposityModel]> = {
    
}()
複製代碼

爲了只產生一個可觀察序列,因此使用懶加載。不須要錯誤處理,因此使用 Driver 對象。網絡

在懶加載內部,將 searchTextOB 做爲可觀察序列訂閱。即在 searchTextOB 收到外界輸入框的輸入變化時,同步發送信號,發起網絡請求。閉包

lazy var searchData: Driver<[BOReposityModel]> = {
    
    return self.searchTextOB.asObservable()
        // 每隔300毫秒請求一直,而且在主線程發起請求 
        .throttle(0.3, scheduler: MainScheduler.instance)
        // 阻止發送相同的信號
        .distinctUntilChanged()
        .flatMapFirst(BOViewModel.requestData)
        // 若是出錯,則返回空數組
        .asDriver(onErrorJustReturn: [])
}()
複製代碼
  • 爲了防止請求過快,因此間隔300毫秒纔在主線程發起一塊兒請求。
  • 除非接收到不同的信號,纔會發起響應。
  • 並且還作了錯誤處理,若是出錯了,則返回空數組。
  • flatMapFirst 函數接收一個閉包做爲參數,但其實閉包也是一個函數,因此這裏傳入一個函數做爲參數也是能夠的。

四、發起網絡請求

本例中使用的請求地址爲:api.github.com/users/(gith…框架

在發起網絡請求以前須要先判斷傳入的參數是否爲空,已經網絡地址是否正確。若是有錯誤,則返回空數組。異步

static func requestData(_ githubId: String) -> Observable<[BOReposityModel]> {
    
    guard !githubId.isEmpty, let url = URL(string: "https://api.github.com/users/\(githubId)/repos") else {
        
        return Observable.just([])
    }
    
    return URLSession.shared.rx.json(url: url)
        .retry()
        .observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
        .map(BOViewModel.parseData)
}
複製代碼

使用 URLSession 發起異步網絡請求,而且對請求的結果進行數據解析。

五、數據解析

class BOReposityModel: HandyJSON {

    var name: String = ""
    var url: String  = ""
    
    required init() {}
}
複製代碼
static func parseData(_ json: Any) -> [BOReposityModel] {
    
    guard let datas = json as? [[String: Any]] else { return [] }
    
    guard let result = [BOReposityModel].deserialize(from: datas) else { return [] }
    
    return result as! [BOReposityModel]
}
複製代碼

這裏使用了一個三方庫 HandyJSON 進行數據解析,小夥伴也能夠根據本身的喜愛和習慣,選擇本身熟悉的數據解析方式。

六、綁定Model到UI

請求到數據之後,就須要將數據展現到頁面上。

一、展現搜索結果數量到導航欄上

// 實現 model -綁定-> view
self.viewModel.searchData.map { (array) -> String in
    return "共\(array.count)個結果"
    }
    .drive(self.navigationItem.rx.title)
    .disposed(by: self.disposeBag)
複製代碼

二、展現結果到tableView列表上

// 實現 model -綁定-> view
self.viewModel.searchData.drive(self.tableView.rx.items){
    (tableView, row, model) in
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! BOTableViewCell
    
    cell.nameLabel.text = model.name
    cell.urlLabel.text = model.url
    
    return cell
    }
    .disposed(by: self.disposeBag)
複製代碼

searchData 做爲請求結果序列,直接驅動 tableView 的顯示刷新。

附個demo圖:UI太醜,勿噴

使用這樣的方式,無需再實現 UITableViewdelegatedataSource 協議方法。是否是簡單不少?

RxSwift已經幫咱們封裝好了UITableView的代理協議,因此咱們能夠更專一於業務層以及UI層面,而再也不須要各類冗餘的代碼。

使用 RxSwift 雙向綁定來實現簡單的搜 Demo,ViewController 中的代碼不過區區60行左右,若是使用非RxSwift的方式,代碼至少百行。這僅僅是計算Controller中的代碼,還不包括ViewModel。這麼好用的框架此時不用,更待什麼時候?

好的框架須要學習,好的技術須要實戰。

相關文章
相關標籤/搜索