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()
})
最後に
もうちょっとちゃんとまとめておきます