FRP(Functional Reactive Programming)をObjective-C(Swift)で書くためのフレームワークであるReactiveCocoa。
ぱっと見た感じ初心者にはとっつきにくさを感じたので勉強録としてまとめていきます。
lv.1
超初心者的なReactiveCocoaの使い方しか書いていないです。(わかんなくて書けなかったです)
ReactiveCocoaの本質に触れられていないのであしからず。
FRPとは
「振る舞い」を定義して値の変更を動的に反映させることだと思っています。
振る舞いとはどういうことかというと
A = 10;
B = 20;
// Cに振る舞いを定義
C = A + B;
A = 20;
と書くとC = 30
ですが、FRPの概念だとC = 40
になります。
それはCには「AとBを足した結果」を定義したわけじゃなく「AとBを足す振る舞い」を定義したからです。
なのでCはAとBの変更を動的に受け取って値(状態)を変えます。
Aに変更があればCへ変更が伝搬されて振る舞いに再度当てはめます。(A + B
という振る舞いへあてはめる)
その結果、C = 40
となります。
FRPで何が良くなるの?
フロントエンドがリッチ化していく中、アプリケーションの中に様々な状態が複数存在しています。
MVCでModel
の変更を様々なView
へ伝えていく処理が複雑になってしまい見通しが悪くなります。
そこでFRPを使うことでModel
の変更の伝搬を簡素化できます。
なので、当たり前ですがすべてのiOSアプリ開発にReactiveCocoaが適している訳ではありません。
一つの選択肢として選べる程に理解しておきたいので今回は勉強したいと思っています。
ReactiveCocoaを触ってみる
本題ですが、ReactiveCocoaをSwiftでみました。
RACSignal
ReactiveCocoaでは前述で説明した変更をRACSignal
を生成して伝搬させます。
将来変更が起こりそうなプロパティ(UITextField.textなど)に対してRACSignal
を作成し、作成したシグナルに「振る舞い」を追加します。すると、変更があった時に振る舞いを伝搬してくれます。
RACSignal
の作成方法は大きく2つ
RACObserve
RACObserve
マクロでオブジェクトのプロパティを監視してRACSignal
を発生させれる。
ただSwiftだとマクロが使えないので下記のようにしなければいけない。
そして、うまく動かなかったので今回はこっちは使わない。
func RACObserve(target: NSObject!, keyPath: String) -> RACSignal {
return target.rac_valuesForKeyPath(keyPath, observer: target)
}
// 動かなかった
let signal = RACObserve(self.textField, "text")
signal.subscribeNext({(any:AnyObject!) -> Void in
self.countLabel.text = "\(any)"
})
rac_textSignal
UITextField
などにはtext
の変更を監視できるrac_textSignal
メソッドが拡張されています。
実行することでRACSignal
が生成できるので振る舞いを書いていきます。
self.textField.rac_textSignal()
.subscribeNext({(any:AnyObject!) -> Void in
self.countLabel.text = "\(any)"
})
textFieldの入力を動的に変更する
self.textField.rac_textSignal()
.map({ (text:AnyObject!) -> AnyObject! in
return (text as? String)?.uppercaseString;
})
.subscribeNext({(text:AnyObject!) -> Void in
self.countLabel.text = text as? String
})
RACCommand,rac_command
UI系に対してアクションを起こした時にRACSignal
を生成するクラス。
rac_command
はUIButton
がUIControlEventTouchUpInside
になっときにexecute
されるRACCommand
をセットできるプロパティ。
self.button.rac_command = RACCommand(signalBlock:{(_:AnyObject!) -> RACSignal in
self.countLabel.text = ""
return RACSignal.empty()
})
最後に
もうちょっとちゃんとまとめておきます