LoginSignup
12
9

More than 3 years have passed since last update.

RxSwift + Action

Last updated at Posted at 2018-06-24

RxSwiftの派生ライブラリActionを使ってみる

RxSwiftの派生プロジェクトにActionというライブラリがあります。
RxSwiftでの動作の定義を入出力をはっきりとさせて管理するものっぽい。
Actionの構成要素は次の通り。

種類 概要
Action 動作定義
Input 入力要素
Element 出力要素
Errors エラー情報

この4つで管理するフォーマットが組める! というのが売りみたいです

よくあるログインの動作を例に

こんなのを作って確かめてください

ViewController.swift
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)

いくつかサンプルを作成してみました。

12
9
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
12
9