【Swift】RxSwiftをTwitterで知り合った人に教えてもらったPart2


はじめに

前回
今回も教えていただいたことをまとめていこうと思います。

実装

ViewController
import UIKit
import RxSwift
import RxCocoa

final class ViewController: UIViewController {

    @IBOutlet private weak var textField: UITextField!
    @IBOutlet private weak var button: UIButton!
    @IBOutlet private weak var label: UILabel!
    private let disposeBag = DisposeBag()
    private var viewModel: ViewModelType = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: - Input
        //bindでViewModelにイベントを送るパターン
        textField.rx.text.orEmpty
            .bind(to: viewModel.inputs.text)
            .disposed(by: disposeBag)

        //onNextで送るパターン
        button.rx.tap
            .subscribe { _ in
                self.viewModel.inputs.text.onNext("タップ")
            }
            .disposed(by: disposeBag)

        // MARK: - Output
        //viewModelからoutputをUIパーツにbindするパターン
        viewModel.outputs.validText
            .bind(to: label.rx.text)
            .disposed(by: disposeBag)

    }

}
ViewModel
import RxSwift
import RxCocoa

protocol ViewModelInput {
    //読み取り専用にしたいため、AnyObserver
    var text: AnyObserver<String> { get }
}

protocol ViewModelOutput {
    //ViewでsubsrcibeするためにObservable
    var validText: Observable<String> { get }
}

protocol ViewModelType {
    var inputs: ViewModelInput { get }
    var outputs: ViewModelOutput { get }
}

class ViewModel: ViewModelInput, ViewModelOutput {
    var text: AnyObserver<String>
    var validText: Observable<String>
    init() {
        let _validText = PublishRelay<String>()
        self.validText = _validText.asObservable()
        self.text = AnyObserver<String>() { text in
            //viewからイベントを受け取り、加工したものをoutputにaccept
            let validText = text.element!
            _validText.accept(validText)
        }
    }
}

extension ViewModel: ViewModelType {
    var inputs: ViewModelInput {
        return self
    }
    var outputs: ViewModelOutput {
        return self
    }
}

イベントの送り方受け取り方

Input(View -> ViewModel)

今回はボタンを押した時とテキストフィールドに文字が入力された時にイベントを流します。

以下はbindでviewModelのinputのtextにイベントを送っています。

textField.rx.text.orEmpty
    .bind(to: viewModel.inputs.text)
    .disposed(by: disposeBag)

こちらはボタンのイベントをviewModelのinputのtextにonNextで送っています。

button.rx.tap
    .subscribe { _ in
        self.viewModel.inputs.text.onNext("タップ")
    }
    .disposed(by: disposeBag)

そして、ViewModelのViewModelInputプロトコルに送るtextを定義しておいて、加工してoutputで返すという流れです。

protocol ViewModelInput {
    var text: AnyObserver<String> { get }
}

Output(ViewModel -> View)

イニシャライザ内で値を加工し、acceptでoutputにイベントを送ります。

class ViewModel: ViewModelInput, ViewModelOutput {
    var text: AnyObserver<String>
    var validText: Observable<String>
    init() {
        let _validText = PublishRelay<String>()
        self.validText = _validText.asObservable()
        self.text = AnyObserver<String>() { text in
            let validText = text.element!
            _validText.accept(validText)
        }
    }
}

viewModelから送られてきたoutputをbindすることで、labelに反映させる。

viewModel.outputs.validText
    .bind(to: label.rx.text)
    .disposed(by: disposeBag)

おわりに

少しずつ分かってきましたね!