RxSwiftの派生ライブラリActionを使ってみる
RxSwiftの派生プロジェクトにActionというライブラリがあります。
RxSwiftでの動作の定義を入出力をはっきりとさせて管理するものっぽい。
Actionの構成要素は次の通り。
種類 | 概要 |
---|---|
Action | 動作定義 |
Input | 入力要素 |
Element | 出力要素 |
Errors | エラー情報 |
この4つで管理するフォーマットが組める! というのが売りみたいです
よくあるログインの動作を例に
こんなのを作って確かめてください
import UIKit
import RxSwift
import RxCocoa
import Action
import NSObject_Rx
class ViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
Actionの定義
第一引数をinput、第二引数をonNextで渡す変数として定義
// ユーザーアカウントとパスワードでログイン
let loginAction: Action<(String,String), Bool> = Action {
(username, password) in
return Observable.just(true) // 実際のアプリでこういうのはないですね APIに投げてリクエスト処理などが入ると思います
}
Action実行前の処理を定義
Actionがコールされる直前の処理を定義 このときにinputに値が渡るようにする
// よくあるユーザーアカウントとパスワードのObservable化
let usernameAndPassword = Observable.combineLatest(usernameTextField.rx.text.orEmpty,
passwordTextField.rx.text.orEmpty)
// ログインボタンがタップされた時に上の値をactionのinputにbind
loginButton.rx.tap.asObservable()
.withLatestFrom(usernameAndPassword)
.bind(to: loginAction.inputs)
.disposed(by: rx.disposeBag)
loginActionの第一引数が(String,String)にユーザーアカウントとパスワードが渡るようにする
変数とinputをbindするものと考えていいっぽい
実行後
inputに値が入った直後にActionが実行される
Actionが実行された後は、実行結果がelementに入る こういうので確かめられる
loginAction.elements
.filter{ $0 }
.subscribe( onNext:{
print($0)
print("login ok!")
}
)
.disposed(by: rx.disposeBag)
エラーが起きた時は errorsを参照
loginAction.errors
.subscribe(
onError:{ error in
print(error.localizedDescription)
}
)
.disposed(by: rx.disposeBag)
補足
Action以外の要素は、入力 / 結果 / エラー を表します。つまりこれらの変数をうまく利用することで、
start / end /error のトリガー処理も一定のフォーマットで記述することができます。
上のログインの例で考えるとこういう例も考えられます。
let startTrigger = PublishSubject<Void>() // 前もってSubjectを定義しておく
loginbutton.rx.tap.asDriver() // ボタンタップでSubjectをキック
.drive(self.startTrigger)
.disposed(by: disposeBag)
// ログイン処理が走る
startTrigger.asObservable()
.withLatestFrom(usernameAndPassword)
.bind(to: loginAction.inputs)
.disposed(by: rx.disposeBag)
ログイン後ではelementを監視して次の処理を定義
let successTrigger: Observable<Bool> // 前もって定義しておく
// loginActionが成功時にonNextに渡すBool値とbind
self.successTrigger = loginAction.elements
// ログイン成功
self.sucessTrigger.asObservable()
.subscribe(onNext: { [weak self] _ in
// ログイン後の画面へ遷移など
})
.disposed(by: disposeBag)
エラー処理はこうすれば楽かも?
loginAction.error.asDriver()
.drive(onNext: { [weak self] error in
let alert = UIAlertController(title: "エラー", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
})
.disposed(by: disposeBag)
Actionの明示的な実行
動作確認などのためにActionを実行したい場合はexcuteメソッドでinputに相当する値を渡す
loginAction.execute(("user","pass"))
.subscribe{
print("loginAction.execute")
print($0)
}
.disposed(by: rx.disposeBag)