はじめに
アプリを使っていて、検索とかで表示レスポンスがリアルタイムなアプリとそうでないアプリがありました。
どうやってるのかなー?と調べてみるとリアクティブプログラミングなるものがあるらしい...
挙動としてはExcelとかがよく例えられます。
こちらの記事がイメージ掴みやすかったです。
リアクティブプログラミングへの理解がイマイチだったのでまとめてみた
で、iOSで実現するためのライブラリとしてはRxSwiftが他の言語にも流用しやすくて、おすすめらしい..
SwiftでReactive Programming
という記事を目にしてから数年立ってました。
今回勉強できる時間があったので、自作アプリをリリースがてらRxSwiftを使ってみて、
リアクティブプログラミングを実践してみようと思いました。
今回リリースしたアプリをベースにして、一部機能を切り出してサンプルとして公開して、
やってみたことを備忘録として記事に起こそうと思います。
元アプリ紹介
よくあるアプリですが、一見電卓に見えてパスコードを入力したら、
隠していた動画リストが出てくるというアプリです。
結婚してムフフな動画も気楽に観れなくなったということもあり、同様のアプリを探してみたのですが、
使い勝手が悪かったり、昔に作られたままメンテがされてないアプリばかりだったので、
「ぼくがかんがえた さいきょう プライバシー動画ビューアーアプリ」として、
勉強がてら自作してみました。(結局このアプリが作ってることは嫁にバレている..)
この手のアプリは「隠す」ことに重点が置かれていて、
動画のファイラーとしての使用感がよろしくないので、
その点についてこだわってみました(動画検索/圧縮機能あり)。
現在は動画は写真アプリかファイルアプリから取り込むしかありませんが、
今後はurlから動画ダウンロード機能も追加していきたいと思います。(Apple的にNGかな?)
生意気にも有料販売しているので、気が向いた方は是非ともお買い求めお願いします!
https://itunes.apple.com/jp/app/id1464652243
目標
あんまり最初からハードル上げないよう
- MVVMアーキテクチャに触る
- RxSwiftを使ってViewControllerとViewModelのバインディングをやってみる
くらいにしました。
サンプル解説
元のアプリは
電卓画面 → パスワードを入れると動画ビューアー出現
というアプリでしたが、この中の電卓機能のみを切り出してサンプル作りました。
サンプルのリンクはこちら
・処理の流れ(MVVMアーキテクチャ)
各クラスの役割
-
CalculatorViewController(View)
ユーザーのインターフェース
ViewModelとデータバインディングし、自動描画する
→ボタンタップをViewModelに伝えて結果を更新 -
CalculatorViewModel(ViewModel)
Viewのための状態保持
ViewからのデータをModelに伝達
→ViewのボタンタップをModel用に加工。結果をまた加工してViewに通知 -
CalculatorModel(Model)
ViewModelから受け取ったデータを処理する
→実際の計算処理
View→ViewModel→Model
という参照/処理の流れで、
ViewはViewModelを参照するが、Modelを参照しない。
ViewModelはModelを参照するが、Viewは参照しない。
Modelは参照されるのみ。
・RxSwiftのライブラリを使った部分
ViewとViewModelの表示のデータバインディング
今回表示系の更新は電卓の入力&計算結果を表示する
displayNumLabel
のみ。
viewModelのBehaviorRelay変数displayNumと下記のようにバインディングさせる。
BehaviorRelayはacceptで値を変更するとき、onNextのイベントを受け取ることができる変数です。
viewModel.displayNum
.asObservable()
.bind(to: displayNumLabel.rx.text)
.disposed(by: disposeBag)
ボタンタップのイベント処理
RxSwiftを利用しない時はタップ等のイベント処理で@IBActionを使用してましたが、
rx.tapを利用すれば@IBActionを利用しなくてもタップを検知し、処理を実行できるようになります。
zeroButton.rx.tap
.subscribe { [weak self] _ in
guard let `self` = self else { return }
// 処理を実行
self.setOperationButtonSelected(tapOperationButton: .none)
self.viewModel.tapNumberButton(inputNum: "0")
}
.disposed(by: disposeBag)
使ってみた感想
RxSwiftを使う前は
1. View入力受け取り
2. View入力受け取り値をViewModelへ伝達
3. ViewModelで処理
4. ViewModelの処理結果をViewへ伝達
5. View更新
というプロセスを経なければならなかったですが、
RxSwiftを使えば、変数がView・ViewModelとバインディングされているので、
1. View入力受け取り
2. View入力受け取り値をViewModelへ伝達
3. ViewModelで処理(勝手にViewへ通知が行き、更新される)
で済むようになりスッキリ書けるようになりました。
今後改善したいこと&疑問
・ ViewModelとModelのバインディング(?)
MVVMではRxSwiftなどのライブラリを用いてViewControllerとViewModelをバインディングするという記事をよく見かけるけど、
ViewModelとModelもバインディングするものなのかな??
計算機の部分のViewModelとModelだとシンプルだから必要ない気がする...
参考にさせていただいた記事
上記以外の参考にさせていただいた記事を載せます。
オブザーバーパターンから始めるRxSwift入門
全体的な流れや概念の部分で参考にさせていただきました。
RxSwift 再入門
イベント処理の流れについて参考にさせていただきました。