RxSwiftのexampleをコードで書く(その２)

#はじめに
RxSwiftのexampleをコードで書く
RxSwift公式のSimpleValidationViewController.swiftの話です
レイアウトはsnapKitを使っています

#環境構築
## プロジェクトファイルを作る
名前は何でもいいですが今回の例では`RxSwift_for_code`としてます

## podfileの編集

```
$ vi Podfile
```

Podfileの中身
`target 'RxSwift_for_code' do`の`RxSwift_for_code`をプロジェクトファイルで使った名前にしてください

```
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'

use_frameworks!

target 'RxSwift_for_code' do
    pod 'RxSwift',    '~> 4.0'
    pod 'RxCocoa',    '~> 4.0'
    pod 'SnapKit', '~> 4.0.0'
end
```

## Podfileに登録したライブラリをおとしてくる (bundlerでcocoapodを管理しているためpodのコマンドを使うときはbundle execを前につける必要がある)

```
$ pod install
```


#どんなサンプルなの？
TextFieldに入れる文字数によってlabelのtextを表示するかしないかを制御できる

ユーザーネームとパスワードどちらとも5文字以上にならないとボタンがおせないような制御ができる


| 文字が５文字以上になる前 | ５文字以上になった時 | ボタンを押すと |
|:------------:|:------------:|:------------:|
|![screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/176479/6a569a6c-04ac-01cf-1114-901beceeb957.png)| ![screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/176479/9c30aa29-a90f-a53e-fca4-815383caacb5.png)| ![screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/176479/ef59cc39-ff34-9b0f-8afa-94a65c79e2a6.png)|

#コードの中身

```Swift
import UIKit
import RxSwift
import RxCocoa
import SnapKit

fileprivate let minimalUsernameLength = 5
fileprivate let minimalPasswordLength = 5

class ViewController: UIViewController {
    var disposeBag = DisposeBag()

    var userName: UILabel = {
        let label = UILabel()
        label.text = "userName"
        return label
    }()

    var userNameField: UITextField = {
        let textField = UITextField()
        textField.borderStyle = .roundedRect
        return textField
    }()

    var userNameValidLable: UILabel = {
        let label = UILabel()
        label.text = "userName"
        return label
    }()

    var password: UILabel = {
        let label = UILabel()
        label.text = "password"
        return label
    }()

    var passwordField: UITextField = {
        let textField = UITextField()
        textField.borderStyle = .roundedRect
        return textField
    }()

    var passwordValidLable: UILabel = {
        let label = UILabel()
        label.text = "password"
        return label
    }()

    var button: UIButton = {
        let button = UIButton()
        button.backgroundColor = UIColor.blue
        button.setTitle("テスト", for: .normal)
        return button
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        self.initializeUILayout()
        userNameValidLable.text = "ユーザー名は\(minimalUsernameLength) 文字以上"
        passwordValidLable.text = "パスワードは\(minimalPasswordLength) 文字以上"

        let userNameValid = userNameField.rx.text.orEmpty
            .map{$0.count >= minimalUsernameLength}
            .share(replay: 1)

        let passwordValid = passwordField.rx.text.orEmpty
            .map{ $0.count >= minimalPasswordLength}
            .share(replay: 1)

        let everythingValid = Observable.combineLatest(userNameValid, passwordValid){ $0 && $1 }
            .share(replay: 1)

        userNameValid.bind(to: passwordField.rx.isEnabled).disposed(by:disposeBag)

        userNameValid.bind(to: userNameValidLable.rx.isHidden).disposed(by:disposeBag)

        passwordValid.bind(to: passwordValidLable.rx.isHidden).disposed(by: disposeBag)
        
        everythingValid.bind(to: button.rx.isEnabled).disposed(by: disposeBag)
        
        button.rx.tap.subscribe(onNext: {[weak self] _ in self?.showAlert()})
        
    }
    
    func showAlert() {
        let alertView = UIAlertView(
            title: "RxExample",
            message: "This is wonderful",
            delegate: nil,
            cancelButtonTitle: "OK"
        )
        
        alertView.show()
    }

    func initializeUILayout() {
        self.view.addSubview(self.userName)
        self.userName.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(10)
            make.centerX.equalTo(self.view)
        }
        self.view.addSubview(self.userNameField)
        self.userNameField.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.userName.snp.bottom)
            make.centerX.equalTo(self.view)
        }
        self.view.addSubview(self.userNameValidLable)
        self.userNameValidLable.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.userNameField.snp.bottom)
            make.centerX.equalTo(self.view)
        }

        self.view.addSubview(self.password)
        self.password.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.userNameValidLable.snp.bottom)
            make.centerX.equalTo(self.view)
        }
        self.view.addSubview(self.passwordField)
        self.passwordField.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.password.snp.bottom)
            make.centerX.equalTo(self.view)
        }
        self.view.addSubview(self.passwordValidLable)
        self.passwordValidLable.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.passwordField.snp.bottom)
            make.centerX.equalTo(self.view)
        }

        self.view.addSubview(self.button)
        self.button.snp.makeConstraints { (make) -> Void in
            make.width.equalTo(300)
            make.height.equalTo(50)
            make.top.equalTo(self.passwordValidLable.snp.bottom)
            make.centerX.equalTo(self.view)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}
```

#調べたことメモ
土管を作っているイメージに近いと思った

## userNameValidのところ

```Swift
let userNameValid = userNameField.rx.text.orEmpty
            .map{$0.count >= minimalUsernameLength}
            .share(replay: 1)
```

### .text
getということは値を取り出せること
`text`の型は`RxCocoa.ControlProperty<String?>`型

```Swift
extension Reactive where Base : UITextField {
/// Reactive wrapper for `text` property.
    public var text: RxCocoa.ControlProperty<String?> { get }
```

### .orEmpty
`.text`次に`.orEmpty`がつながる

定義はこのようになっている
変数`orEmpty`はgetなのでString型が中にはいったControlProperty型の値を取り出せる
```Swift
extension ControlPropertyType where Self.E == String? {

    /// Transforms control property of type `String?` into control property of type `String`.
    public var orEmpty: RxCocoa.ControlProperty<String> { get }
}
```
また`ControlPropertyType`型のextensionで`where Self.E == String?`の`Self`は`ControlPropertyType`を指す

定義に飛ぶと下記のようになっている

```Swift
/// Protocol that enables extension of `ControlProperty`.
public protocol ControlPropertyType : ObservableType, ObserverType {

    /// - returns: `ControlProperty` interface
    func asControlProperty() -> ControlProperty<E>
}

//////省略///////

public struct ControlProperty<PropertyType> : ControlPropertyType {
    public typealias E = PropertyType

    let _values: Observable<PropertyType>
    let _valueSink: AnyObserver<PropertyType>
//////省略///////
```

`where Self.E == String?`の`E`は`public typealias E = PropertyType`のEを指す
今回の例では具体的に`orEmpty`をつなげるため定義を再び見てみると下記のようになっていて
`orEmpty: RxCocoa.ControlProperty<String>`
`ControlProperty<E>`と一致している


### .map{$0.count >= minimalUsernameLength}
map関数では流れてきたデータをそれぞれ比較している

#### .count
$0にはStringの型のデータがはいってくる、流れてきたデータ(String型)に対して、その数を数えてIntで返す関数である

```Swift
extension String {
 public var count: Int { get }
}
``` 
この数とminimalUsernameLengthを比較し,trueとfalseを流していく

### .share(replay: 1)
ここが参考になりました
[[RxSwift] shareReplayをちゃんと書いてお行儀良くストリームを購読しよう](https://qiita.com/kazu0620/items/bde4a65e82a10bd33f88)

```
複数のObserverが購読してるストリームで、最初の例の様に2本のストリームを作るのではなく、
1本ストリームから同じ値を購読したいんだ、という場合には不必要な処理や意図しない処理が走らないように、shareReplay(share)をつけておきましょう！
```
ということでした。

この土管ではStringの入力に対して、文字数を数え、その文字数によってtrueかfalseに変換されるという流れができていることがわかりました


## userNameValidを購読するところ
先ほどできた土管を使ってほかの土管を制御している

### .bind(to: passwordField.rx.isEnabled).disposed(by:disposeBag)
userNameValidはtrueかfalseが流れてくるはず
このtrueかfalseの値を使ってisEnabled(触れる),isHidden(隠れる)のプロパティを制御している

新しいサブスクリプションを作成し、オブザーバに要素を送信します。
- パラメータ：イベントを受け取るオブザーバー。
(ここではpasswordFieldのisEnabledのプロパティのこと)
- returns：オブザーバの登録を解除するために使用できる使い捨てオブジェクト。
(返り値は登録を解除できるオブジェクトを返すから後ろに.disposedをつなげることができる)

```Swift
extension ObservableType {
    public func bind<O>(to observer: O) -> Disposable where O : ObserverType, Self.E == O.E

```

### .isEnabled
定義を確認すると`Binder<Bool>`の型のプロパティであることがわかる

```Swift
extension Reactive where Base: UIControl {
    
    /// Bindable sink for `enabled` property.
    public var isEnabled: Binder<Bool> {
        return Binder(self.base) { control, value in
            control.isEnabled = value
        }
    }
```

## buttonのtapの制御のところ
button.rx.tap.subscribe(onNext: {[weak self] _ in self?.showAlert()})
ボタンがタップされたときにどんな関数を実行するのかクロージャー内にまとめている

### .tap
UIButtonクラスを拡張するReactiveextension
tapの型は`ControlEvent<Void>`で流れてくるのはVoidの型

```Swift
extension Reactive where Base: UIButton {
    
    /// Reactive wrapper for `TouchUpInside` control event.
    public var tap: ControlEvent<Void> {
        return controlEvent(.touchUpInside)
    }
}
```

### subscribe
subscribeの定義は下記のようになっている

```Swift
public func subscribe(onNext: ((Self.E) -> Swift.Void)? = default, onError: ((Error) -> Swift.Void)? = default, onCompleted: (() -> Swift.Void)? = default, onDisposed: (() -> Swift.Void)? = default) -> Disposable
```

第一引数は`onNext: ((Self.E) -> Swift.Void)? = default`
引数はクロージャー、もし引数がなければdefaultが設定されるようになっているため、この引数はなくてもいい
第二引数は`onError: ((Error) -> Swift.Void)? = default`
今回は使わなかったが失敗した場合はここにクロージャーを入れるといい感じに処理をしてくれるのだろう

### {[weak self] _ in self?.showAlert()}
クロージャー内でリファレンスカウンタを増やさないために弱参照を設定している
引数は`onNext: ((Self.E) -> Swift.Void)? = default`より`(Self.E)`具体的にはtap`ControlEvent<Void>`でVoidなのではないかな・・？
このクロージャー内部ではこの引数は使わないので '_'で引数は存在するが名前をつけないようにしている
弱参照のため、クラス内の関数を呼び出す際は`self`ではなく`self?`をつけて呼んでいる

# 参考文献
[[RxSwift] shareReplayをちゃんと書いてお行儀良くストリームを購読しよう](https://qiita.com/kazu0620/items/bde4a65e82a10bd33f88)

理解不足が多々あると思うので
間違っていたらご指摘お願いします