LoginSignup
0
1

More than 3 years have passed since last update.

【Swift】RxSwift勉強してみたPart6

Last updated at Posted at 2021-04-11

はじめに

前回
今回はWKWebViewを使った以下のような簡単なアプリを作ります。
スクリーンショット 2021-04-12 0.43.47.png

GitHub

WebViewRxSwiftフォルダにあります

実装

import UIKit
import RxSwift
import RxCocoa
import RxOptional
import WebKit

final class WKWebViewController: UIViewController {

    @IBOutlet private weak var webView: WKWebView!
    @IBOutlet private weak var progressView: UIProgressView!

    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupWebView()

    }

    private func setupWebView() {
        let loadingObservable = webView.rx.observe(Bool.self, "loading")
            .filterNil()
            .share()

        loadingObservable
            .map { return !$0 }
            .bind(to: progressView.rx.isHidden)
            .disposed(by: disposeBag)

        loadingObservable
            .bind(to: UIApplication.shared.rx.isNetworkActivityIndicatorVisible)
            .disposed(by: disposeBag)

        loadingObservable
            .map { [weak self] _ in
                return self?.webView.title
            }
            .bind(to: navigationItem.rx.title)
            .disposed(by: disposeBag)

        webView.rx.observe(Double.self, "estimatedProgress")
            .filterNil()
            .map { return Float($0) }
            .bind(to: progressView.rx.progress)
            .disposed(by: disposeBag)

        let url = URL(string: "https://www.google.com/")
        let urlRequest = URLRequest(url: url!)
        webView.load(urlRequest)
    }    

}

解説

filterNil()

RxOptionalライブラリを利用することで使えるようになります。
nilの場合は値を流さず、nilでない場合はアンラップして値をながすオペレーターです。

RxOptionalを使わない場合

Observable<String?>
    .of("1", nil, "3")
    .filter { $0 != nil }
    .map { $0! }
    .subscribe { print($0) }

RxOptionalを使う場合

Observable<String?>
    .of("1", nil, "3")
    .filterNil()
    .subscribe { print($0) }

share()

ColdなObservableをHotなObservableに変換するオペレーター

以下のコードはtextFieldへのテキスト入力を監視し、ストリームの途中で値を加工して複数のlabelへbindしています。

let text = textField.rx.text
    .map { text -> String in
        print("call")
    }

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

text
    .bind(to: label2.rx.text)
    .disposed(by: disposeBag)

text
    .bind(to: label3.rx.text)
    .disposed(by: disposeBag)

ここで、textFieldに123と入力すると、"call"は9回呼ばれます。値を入力するたびにmapが3回呼ばれるためです。これは、データーベースアクセスするもの、通信処理が発生するものでは好ましくありません。

なぜこの現象が起こるのかをみていきたいと思います。

textField.rx.textControlProperty<String?>として定義されています。
ControlPropertyはUI要素のプロパティで使われ、メインスレッドで値が購読されることが保証されているColdなObservableです。
ColdなObservableとは、subscribe(bind)した時点で読み込まれ、複数回subscribeするとその都度ストリームが生成されるという仕組みです。
今回は3回subscribe(bind)したので、3個のストリームが生成されます。すると、値が変更されたときにオペレーターが3回実行されてしまいます。
これを解決するには複数回購読してもオペレーターを一回実行するだけで済ませれば良さそうです。つまり、ColdなObservableをHotなObservableに変換します。そのためにshare()と言うオペレーターを使います。

let text = textField.rx.text
    .map { text -> String in
        print("call")
    }
    .share()

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

text
    .bind(to: label2.rx.text)
    .disposed(by: disposeBag)

text
    .bind(to: label3.rx.text)
    .disposed(by: disposeBag)

こうすることで、123と入力したときに"call"が三つだけ出力されていたら成功です。

おわりに

次回
RxSwift少しずつわかってきました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1