TodoAPPでRxSwift入門[part2]

#概要
最近RxSwiftを勉強し始めて現在理解していることを備忘録として残せたらいいなと思い記事にします。
そもそもRxSwiftのRxとは

>Rx(Reactive X)とは、「オブザーバパターン」「イテレータパターン」「関数型プログラミング」の概念を実装している拡張ライブラリです。
Rxを導入するメリットは、「値の変化を検知できる」「非同期の処理を簡潔に書ける」ということに尽きると思います。 値の変化というのは変数値の変化やUIの変化も含まれます。 例えばボタンをタッチする、という動作もボタンのステータスが変わったと捉えることができRxを使って記述することができます。

とのことです。
詳しくは以下のサイトを参照してください。
入門！RxSwift
RxSwiftについてようやく理解できてきたのでまとめることにした(1)

今回はPart２になります。
前回の記事はこちら
[TodoAPPでRxSwift入門[part1]](https://qiita.com/apapapa/items/5397470a5856978fd0e4)

#画面構成
![Simulator Screen Shot - iPhone 11 - 2021-03-02 at 09.01.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567404/8132f5a0-f05c-e463-83d9-8c59dd1832a8.png)


画面はこんな感じにします。
タイトルを入力するための`UITextField`と詳細を入力するための`UITextView`とTodoを登録するための`UIButton`をスタックビューに入れてあげてます。

画面上部の`Welcome to Simple Todo App`はただのデザインで入れてみただけなのでなくても大丈夫です。
下部の`Show Todo List`は登録したTodoを見るためのボタンになっています。

UIの配置が面倒な人はこの記事が完結した時にgithubにあげるのでそこのStoryboardからコピペしてください。

#AddTodoViewModel
前回の記事の最後に予告した`ViewModel`の作成に移ります。
そもそも`ViewModel`とは、なんでもかんでもViewControllerに書いちゃったらViewControllerがめっちゃ長くなって見にくいしあまりよろしくないよねということからMVVMというアーキテクチャが編み出されてそれのVM（ViewModel）の部分に当たるものです。
詳しいことはググってください。

では早速コードのほうを書いていきましょう。

```swift:AddTodoViewModel.swift
import Foundation
import RxCocoa
import RxSwift

protocol AddTodoViewPresentable {
    typealias Input = (
        titleText: Driver<String>,
        detailText: Driver<String>
    )
    typealias Output = (
        isValid: Driver<Bool>, ()
    )
    
    var input: AddTodoViewPresentable.Input { get }
    var output: AddTodoViewPresentable.Output { get }
}
```

これで全部ではないですがとりあえずここまでで一旦解説を挟みます。
まず`protocol`？と思われた方もいるかもしれないですがこれはプログラムを疎結合にするためです。疎結合とは？の方は
>[iOSアプリの基本設計を考える：疎結合の概念から構造化、MVVM、RxSwiftまで](https://qiita.com/BlueEventHorizon/items/a712e6fa9fcdc1bdd4b5)

を参照してください。

typealiasで`Input`(入力を受けとる)と`Output`（受け取った値を変換したりして返す）を作っています。ちなみに`tapple`は要素が一つだけだとエラーになるので`Output`には`isValid`と`()`にしています。
次に`Driver`の説明をします。
Driverとは
・エラーを流さない
・メインスレッドでの通知が保証される
・shareReplayLatestWhileConnectedを使ったCold-Hot 変換
の特徴を持ったRxSwift(RxCocoa)独自のものらしいです。

今度はAddViewModelの中身です。

```swift:AddTodoViewModel.swift

class AddTodoViewModel: AddTodoViewPresentable {
    var input: AddTodoViewPresentable.Input
    var output: AddTodoViewPresentable.Output
    
    var storeManager: StoreManager
    
    init(input: AddTodoViewPresentable.Input, storeManager: StoreManager) {
        self.input = input
        self.output = AddTodoViewModel.output(input: self.input)
        self.storeManager = storeManager
    }
}

private extension AddTodoViewModel {
    static func output(input: AddTodoViewPresentable.Input) -> AddTodoViewPresentable.Output {
        let titleObservable = input.titleText.asObservable()
        let detailObservable = input.detailText.asObservable()
        
        let isValid = Observable.combineLatest(titleObservable, detailObservable) { (title, detail) -> Bool in
            return !title.isEmpty && !Validator.removeSpaceAndNewLine(text: detail).isEmpty
        }.asDriver(onErrorJustReturn: false)
        
        return (
            isValid: isValid, ()
        )
    }
}

extension AddTodoViewModel {
    func insertTodoToFireStore(title: String, detail: String) {
        storeManager.insertTodoToFireStore(title: title, detail: detail)
    }
}


```

ここはそこまで説明いらないかなと思いますが、　ViewModelを先程のPresentableに準拠させていないとエラーになるので`fix`でもおしておいてください。
`output`を初期化するためのoutput関数ですが外部から利用されないようにするためと読みやすくするために`private extension`で拡張します。
また、`static`がついているのは、ついていないと変数の`output`が初期化されていないのにそんなの使えないよと怒られるのでこうしています。
`init`でクロージャでもいいとは思いますがそれだと`init`が長くなってしまうので今のところこれがベストかと思いこうしています。

`output関数`の中を少し解説します。
inputで受け取った値を`asObservable()`でObservableに変えています。

そして`combineLatest(observable1, observable2)`は
>（違う型でも可の）直近の最新値同士を組み合わせたイベントを作ります。

引数に指定した値のどちらかがが変化すれば{}ないの処理を実行する感じですかね？

あと、さらっと`Validator`というのが出ていますがこれは`detail`が空白や改行のみだと`false`を返します。
コードはこちらです。

```swift:Validator.swift
import Foundation

class Validator {
    
    static func removeSpaceAndNewLine(text: String) -> String {
        var removeText = text.trimmingCharacters(in: .init(charactersIn: " "))
        removeText = removeText.trimmingCharacters(in: .init(charactersIn: "　"))
        removeText = removeText.trimmingCharacters(in: .init(charactersIn: "\n"))
        return removeText
    }
    
}

```
Stringを返していますがBoolを返せばよかったと思います。

#まとめ
正直あまり理解できていないところもあり、まだまだ勉強しないといけないことだらけで泣きそうです。
疎結合なプログラムも連鎖的なバグを防ぐためなのはなんとなく理解できていますが、その恩恵を得られるほどのプログラムを書いた経験がないのでパッとしないです。
もっと勉強して就職して恩恵を感じたいですね。
次回はViewControllerになります。

[TodoAPPでRxSwift入門[part3]](https://qiita.com/apapapa/items/a65c0a617a7149202ca9)