53
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SwiftTask(Promise拡張)でリアクティブプログラミング

Posted at

前回の

  1. Swiftで有限オートマトン(ステートマシン)を作る - Qiita
  2. Swift+有限オートマトンでPromiseを拡張する - Qiita
  3. SwiftTask(Promise拡張)を使う - Qiita

の続きです。まだこのネタ引きずってました

Objective-C時代のリアクティブプログラミング用のOSX/iOSフレームワークといえば、ReactiveCocoaがデファクトスタンダードですが、使いこなせると強力な一方で、パッと見、複雑そうで敬遠している方も多いと思います(私もつい最近までそうでした)。

実際に、GitHubのソースを追うのにも一苦労し、RACDisposableがこれこれこうなって・・・なるほどわからん :older_man: 状態になることが少なくありません。

そこで今回、Swiftでもっと簡単に、カジュアルにリアクティブプログラミングを実装したいということで、ReactKitというライブラリを作りました。

ReactKit
https://github.com/inamiy/ReactKit

使用例

まずは簡単で分かりやすい KVO/KVCの例を見ていきます。

// create signal via KVO
self.obj1Signal = KVO.signal(obj1, "value")

// bind signal via KVC (<~ as binding operator)
(obj2, "value") <~ self.obj1Signal

XCTAssertEqual(obj1.value, "initial")
XCTAssertEqual(obj2.value, "initial")

obj1.value = "REACT"

XCTAssertEqual(obj1.value, "REACT")
XCTAssertEqual(obj2.value, "REACT")

obj1.valueの変更に合わせて、obj2.valueが自動的に連動しているのが分かります。

Signalの中身は、実は先日のQiita記事で作ったSwiftTaskを使用していて、Taskのサブクラスという形でthen-ableな設計になっています。

その代償として、Promise Chaining優先のためにRACDisposableクラス等が省かれており、 bindingを解除する際は、signalごと解放します。

self.obj1Signal = nil   // release signal & its bindings

obj1.value = "Done"

XCTAssertEqual(obj1.value, "Done")
XCTAssertEqual(obj2.value, "REACT")  // not reacting

2014/09/30現在、Signalとしてまともに使えるのは、

  • KVO
  • NSNotification
  • 一部のUIControl

のみですが、その他Foundation/AppKit/UIKitクラスのSignal化も随時行っていきます。

より実践的な例

QiitaにReactiveCocoaのとても良いサンプルがあったので、拝借させていただきました。

4つのUITextFieldの入力値とUIButtonenabledを連動させる処理をReactKitで書くと、こうなります。

let usernameTextSignal = self.usernameTextField.textChangedSignal()
let emailTextSignal = self.emailTextField.textChangedSignal()
let passwordTextSignal = self.passwordTextField.textChangedSignal()
let password2TextSignal = self.password2TextField.textChangedSignal()

let anyTextSignal = Signal.any([usernameTextSignal, emailTextSignal, passwordTextSignal, password2TextSignal])

// create button-enabling signal via any textField change
self.buttonEnablingSignal = anyTextSignal.map { (values, changedValue) -> NSNumber? in

    let username: NSString? = values[0]?
    let email: NSString? = values[1]?
    let password: NSString? = values[2]?
    let password2: NSString? = values[3]?

    // validation
    let buttonEnabled = username?.length > 0 && email?.length > 0 && password?.length >= MIN_PASSWORD_LENGTH && password? == password2?

    // NOTE: use NSNumber because KVO does not understand Bool
    return NSNumber(bool: buttonEnabled)
}

// REACT: enable/disable okButton
(self.okButton, "enabled") <~ self.buttonEnablingSignal!

map(f: T -> U)を使って、anyTextSignalが変更値として返す(values, changedValue) tuple型をチェックし、UIButtonenabledがKVCで解釈できるNSNumberに変換しています。


以上、簡単な紹介でしたが、もし良かったらStarやPull Reqをいただけると嬉しいです。

53
52
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
53
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?