TodoAPPでRxSwift入門[part3]
概要
最近RxSwiftを勉強し始めて現在理解していることを備忘録として残せたらいいなと思い記事にします。
そもそもRxSwiftのRxとは
Rx(Reactive X)とは、「オブザーバパターン」「イテレータパターン」「関数型プログラミング」の概念を実装している拡張ライブラリです。
Rxを導入するメリットは、「値の変化を検知できる」「非同期の処理を簡潔に書ける」ということに尽きると思います。 値の変化というのは変数値の変化やUIの変化も含まれます。 例えばボタンをタッチする、という動作もボタンのステータスが変わったと捉えることができRxを使って記述することができます。
とのことです。
詳しくは以下のサイトを参照してください。
入門!RxSwift
RxSwiftについてようやく理解できてきたのでまとめることにした(1)
今回はPart3になります。
前回の記事はこちら
TodoAPPでRxSwift入門[part2]
AddTodoViewController
前回の予告通りViewControllerの中身をみていきましょう。
import UIKit
import RxSwift
import RxCocoa
class AddTodoViewController: UIViewController {
@IBOutlet weak var titleField: UITextField!
@IBOutlet weak var detailView: UITextView!
@IBOutlet weak var joinButton: UIButton!
@IBOutlet weak var showTodoListButton: UIButton!
let disposeBag = DisposeBag()
private var viewModel: AddTodoViewPresentable!
override func viewDidLoad() {
super.viewDidLoad()
viewModel = AddTodoViewModel(input: (
titleText: titleField.rx.text.orEmpty.asDriver(),
detailText: detailView.rx.text.orEmpty.asDriver()
), storeManager: StoreManager.shared)
setupViews()
setupBinding()
}
}
private extension AddTodoViewController {
private func setupViews() {
titleField.borderStyle = .none
}
private func setupBinding() {
viewModel.output.isValid
.drive(joinButton.rx.isEnabled)
.disposed(by: disposeBag)
viewModel.output.isValid.drive(onNext: { [weak self] isValid in
self?.joinButton.backgroundColor = isValid ? .init(red: 200/255, green: 200/255, blue: 255/255, alpha: 1) : .lightGray
}).disposed(by: disposeBag)
titleField.rx.controlEvent(.editingDidBegin).asDriver().drive(onNext: { [weak self] in
self?.firstResponderAnimate()
}).disposed(by: disposeBag)
titleField.rx.controlEvent(.editingDidEnd).asDriver().drive(onNext: { [weak self] in
self?.resignFirstResponderAnimate()
}).disposed(by: disposeBag)
detailView.rx.didBeginEditing.asDriver().drive(onNext: { [weak self] in
self?.firstResponderAnimate()
}).disposed(by: disposeBag)
detailView.rx.didEndEditing.asDriver().drive(onNext: { [weak self] in
self?.resignFirstResponderAnimate()
}).disposed(by: disposeBag)
joinButton.rx.tap.subscribe(onNext: { [weak self] in
guard let title = self?.titleField.text, let detail = self?.detailView.text else { return }
self?.viewModel.insertTodoToFireStore(title: title, detail: detail)
self?.titleField.text = ""
self?.detailView.text = ""
}).disposed(by: disposeBag)
showTodoListButton.rx.tap.subscribe(onNext: { [weak self] in
let viewController = TodoListViewController.instantiate()
let navigationController = UINavigationController(rootViewController: viewController)
navigationController.modalPresentationStyle = .fullScreen
self?.present(navigationController, animated: true)
}).disposed(by: disposeBag)
}
private func firstResponderAnimate() {
let width = view.frame.size.width
UIView.animate(withDuration: 0.3) { [weak self] in
self?.view.transform = CGAffineTransform(translationX: 0, y: -width / 4)
}
}
private func resignFirstResponderAnimate() {
UIView.animate(withDuration: 0.3) { [weak self] in
self?.view.transform = .identity
}
}
}
まずViewModel
を初期化しているところからいきます。
titleText
, detailText
にそれぞれの入力するUIを紐付けます。
orEmpty
はアンラップ的な役割になります。
Input = (
text: Driver<String>, ()
)
があったとしてString
を指定しているのに実際流れてくるのはString?
なのでorEmpty
がないとエラーになると思います。
次にsetupBinding
の中をみていきます。
drive
はbind(to: )
のようなものです。
viewModel
のisValid
の値とjoinButton.rx.isEnabled
を紐付けています。
これによって入力の文字数が足りていなかったりした時にボタンが押せないようにしたりできます。
次にcontrolEvent
ですがこれは入力モードになった時にonNext
で画面を少し上にあげるというような処理になります。
textView
も同様ですが、controlEvent
ではなく直接どうなったかの状態を指定する形?になっています。
ボタンのところはタップした時の処理ですね。
タップしたらFirestoreにデータを保存しています。
まとめ
今回の記事を書いて思ったことはFatViewController
を避けるためのMVVMなのに割とFatになってしまったなという感じです。
Input
でボタンがタップされたかどうかをobserveしてデータ保存の処理とかを完全にViewModelに任せてしまいたいですがまだまだ勉強が足りないですね。
また画面遷移はcoordinatorパターン
ですると画面遷移の処理もViewModelで検知して画面遷移するというのもできそうな気がするのですが、MVVMのそれぞれの責務を考えたときそれはViewの仕事なのかな?とも思います。
難しいところですが精進します。
次回は保存したTodoを表示します。
多分次回で終わりにします。
改善できるところや指摘がございましたらよろしくお願いします。
次はこちら。
TodoAPPでRxSwift入門[part4]
Author And Source
この問題について(TodoAPPでRxSwift入門[part3]), 我々は、より多くの情報をここで見つけました https://qiita.com/apapapa/items/a65c0a617a7149202ca9著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .