Edited at

RxSwiftのExamplesにしれっと入ってる双方向データバインディングの演算子がイケてた

More than 1 year has passed since last update.

こんばんは!:octocat:

RxSwift (RxCocoa)を使ってモデル←→ビューのお互いの更新を伝え合う双方向データバインディングをやってみます。

SwiftBond では双方向データバインディングがサポートされているのですが RxSwiftのドキュメントにはそのような記述はなく、綺麗に書く方法ないかな〜とExamplesを眺めていたらそれっぽいものを見つけたのでご紹介します。


やってること

hoge.gif


  • 登場人物


    • ラベル


      • 最新の値を表示し続ける




    • テキストフィールド ←こいつに双方向bindを実装します


      • 最新の値を表示し続ける

      • 値を入力できる



    • ボタン


      • 値を初期値に変更できる





このUIにおいて、



  • テキストフィールドで入力した内容が即時ラベルに反映される。

  • ボタンの押下で書き換わった内容が即時テキストフィールドに反映される。

という点において双方向のデータバインディングを実現してみます。


Before

import UIKit

import RxSwift
import RxCocoa

class ViewController: UIViewController {
private var item = Variable<String?>("Hello!")
private let disposeBag = DisposeBag()

@IBOutlet weak var label: UILabel!
@IBOutlet weak var textField: UITextField!

@IBAction func resetButtonHandler(_ sender: UIButton) {
item.value = "Hello!"
}

@IBAction func textFieldHandler(_ sender: UITextField) {
item.value = sender.text
}

override func viewDidLoad() {
super.viewDidLoad()
setup()
}

private func setup() {
// モデルからビューへ
item.asObservable().bind(to: textField.rx.text).disposed(by: disposeBag)
// ビューからモデルへ
textField.rx.text.subscribe(onNext: { [weak self] text in
self?.item.value = text
}).disposed(by: disposeBag)

// 最新の値をラベルで表示しておく
item.asObservable().bind(to: label.rx.text).disposed(by: disposeBag)
}
}

ここで Bidirectional Operator <-> の登場です。

左辺にビュー、右辺にモデルが来るように書きます。

さらにそのままだと返り値が未使用と言われるので _ = を先頭につけておきます。

(一応例にもそう書いてあったのですが、万が一メモリリークの危険性踏んでたら教えて頂きたく...! :bow:


After

import UIKit

import RxSwift
import RxCocoa

class ViewController: UIViewController {
private var item = Variable<String?>("Hello!")
private let disposeBag = DisposeBag()

@IBOutlet weak var label: UILabel!
@IBOutlet weak var textField: UITextField!

@IBAction func resetButtonHandler(_ sender: UIButton) {
item.value = "Hello!"
}

@IBAction func textFieldHandler(_ sender: UITextField) {
item.value = sender.text
}

override func viewDidLoad() {
super.viewDidLoad()
setup()
}

private func setup() {
_ = textField.rx.text <-> item // ここ(´∀` )
item.asObservable().bind(to: label.rx.text).disposed(by: disposeBag)
}
}

ユーザID+パスワードや、住所氏名年齢電話番号・・・のような設定するコンポーネントが多い画面で活躍してくれそうです!

だんだんRxSwiftが面白くなってきた今日このごろです:relieved: おやすみなさい。