LoginSignup
7
3

More than 5 years have passed since last update.

RxSwift のイベントを制約してみた (Generics Extension & String Constraint)

Last updated at Posted at 2016-12-18

Origami というところの Advent Calender 19日目の記事です。
本当は 12日目の記事だったはずなんですが、
旬だからという理由でここに交代をお願いされました↓
Amazon Dash Buttonで非常ベルみたいなことをやってみた

はじめに

OrigamiではSwift始めたときから、MVVM を取り入れ始めていましたが、
当初はReactiveCocoaいいんじゃね?っていう界隈の流れがありつつも、
Objective-C製からSwift製へ移行していたこともあり、
お手製のFRPっぽく書けるバイディングツールを作って使っていました。

ところが、Swiftアップデートのたびにあやしい動きを見せるReactiveCocoaにとってかわり
RxSwiftが古きから目覚め云々...

ということで、最近RxSwift使ってみています。

RxSwift.Variable を使ってみる

RxSwiftの中でもVariableというプロパティ監視する用のやつが
好きになれそうです。
(ReactiveCocoa でいうところの MutableProperty です)

すごいかんたんなsetup
var nameLabel: UILabel
var name: Variable<String> = Variable("dongri")

var disposeBag = DisposeBag()

func setBinding() {
  name.asObservable().subscribe(onNext: { [weak self] in
    // あたらしい値をLabelに反映
    self?.nameLabel.text = $0
  }).addDisposableTo(disposeBag)
}
dongriさんがdashButtonを押したので、VPにupdateします
func updateToVP() {
  name.value = "takashi"
}

簡単な例ですが、updateToVP()で行なっている name.value = "takashi"
この代入を行うとあらかじめbindされたlistenerへイベントが発火されて
nameLabel のテキストが変更されます。
ごく一般的なリアクティブなアプローチです。

ただこの代入について、よく思うことがあり、
イベント発火しすぎるのを避ける為しばしば
こう書きたくなることがあります。

// 値に変更があったときだけ代入します
func updateToVP() {
  let VPName = "takashi"
  if name.value != VPName {
    name.value = VPName
  }
}

ただ、実際のViewModelではプロパティが多く、
代入に3行かけたくない。

ということでVariableを拡張してみます

拡張 & String制約

protocol StringConvertible {}
extension String: StringConvertible {}

extension Variable where Element: StringConvertible {

  func assignIfNeeded(_ right: String) {
    if let v = self.value as? String, v != right {
      self.value = (right as? Element)!
    }
  }
}

わざわざStringConvertibleを定義するのは、
StringはConcreteTypeなため、Genericsの拡張に対して制約を課せないらしい。。

そのためProtocolTypeを作ってStringにimplementしてます。

その辺は、こちらが参考になりました。
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160229/011666.html
http://stackoverflow.com/questions/39735753/swift-3-generic-extension-arguments

そのままString制約もいつかできようになるといいですね(切実)

上記の拡張を作っておくと、updateToVP() は以下のように書けるようになりました。

func updateToVP() {
  name.assignIfNeeded("takashi")
}

これで値に変更があった場合のみ、イベントが飛ぶようになりました。

ただ、注意しなければいけないのは、Viewの生成のタイミング如何によって
思わぬところでイベントが飛んでこないというパターンが発生するので、
初期化時にすでに取得できている値をちゃんと入れておく等の処理が必要になります。
その辺はデータの持ち方によるのでよ〜く考えてカマしてみましょう。

12日目の記事のslack通知にもこのようなロジックが入れば、
DashButtonで通知が飛びすぎなくていいですね:poop:

We are hiring

多分採用されると思うから応募しようぜ
https://www.wantedly.com/companies/origami/projects

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3