#はじめに
前回
今回はWKWebViewを使った以下のような簡単なアプリを作ります。
#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.text
はControlProperty<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少しずつわかってきました。