【RxSwift】TextFieldの初期値設定やバリデーションチェック(Scanオペレータ)をViewModelから行う【iOS/Swift】

2021年11月21日

概要

この記事では、MVVMアーキテクチャでiOSアプリを作る場合において、テキストフィールドを使う際に個人的によく使うユースケースをまとめています。

まず、テキストフィールドを使用する時に必要そうな要件は、ざっくりと以下のような感じだと思います。

  • テキストフィールドの初期値を設定
  • 入力文字列のバリデーションチェックを行い、問題なければそのまま入力を許可、異常値であれば拒否(例えば数値のみ、アルファベットのみ、10文字以下、などの入力制御)
  • ViewModel側でテキストフィールドに入力された文字列を保持する

図で表すとこんな感じです。

図:テキストフィールドの状態(View <-> ViewModel)

順番に実装方法を見ていきたいと思います。

ViewModelからViewにテキストフィールドの初期値を渡す

ここでは双方向バインディングを使用しています。理由は以下の通りです。

  • View の入力値を ViewModel 側に渡したい(通常のデータバインディング)
  • ViewModel 側で設定した初期値を View 側に渡したい

双方向バインディングを実現するためには、RxSwift の公式サンプルコードに「<->」という演算子が定義されているのでこれを使います。
このコードが書かれたファイルをそのままプロジェクトにインポートすることで使用できるようになります。

https://github.com/ReactiveX/RxSwift/blob/main/RxExample/RxExample/Operators.swift

次に、シンプルなViewとViewModelを定義して、View側のテキストフィールドとViewModel側のRelayを双方向バインディングします。

View:


ViewModel:

なお、UI周りのデータバインディングを実施するため、エラーを流さないように Subject ではなく Relayを使用しています。
(万が一UI周りのストリームでエラーを流してしまうと以降操作不能になることが懸念されるため)

テキストフィールドのバリデーションチェック

テキストフィールドに文字が入力されるたびに、文字列全体の正当性を判定して入力制御を実施します。
ユースケースとしては以下のような感じです。

  • 入力文字数を制御したい(例:10文字以内で入力させたい)
  • 英数字のみ入力させたい

このようなロジックはViewModel側で持たせて、View側はそのバリデーション結果に応じて「新しい文字列」or「前の文字列」のどちらかをテキストフィールドにセットする、という責務分割ができていることが理想だと考えます。

ソースコードは以下の通りです。

View:

Scan オペレータの概要は以下に記載されています。
Scan

ここでは応用的な使い方をしていて、「previous」に前回入力されたテキスト、「newText」に最新テキストが格納されています。
newText の方を「ViewModel.validation」メソッドに渡してバリデーションチェックをしています。


ViewModel:

参考文献

RxSwift Reverse observable aka two way binding

How to implement shouldChangeTextIn the RxSwift way

スポンサードリンク