概要
RxSwiftの基本的な部分について、インターン期間に深く学ぶことができたので自分の知っている範囲でまとめてみたものになります。RxSwift+MVVMでコードを書く際に意識したいことについても少し記載しておきます。
Observableについて
初めに、RxSwiftを書く際によく見かけるObservableについてです。Observableとは、
シーケンス要素を非同期的に受信できるもので、観測可能、イベントを検知できるもの
です。
Observableにデータの流れが加わることで、ストリームとして扱われることになります。
Observableが通知するもの
Observableが通知するものには以下の4つがあります。
onNext
通常のイベント通知、値を入れることができる、何回も呼ばれる
onError
エラー通知、一回のみ、呼ばれた時点で終了
onCompleted
完了を通知、一回のみ、呼ばれた時点で終了
onSubscribe
購読されたタイミングを拾うことができる
Subscribeについて
subscribeといえば、購読するということが多いと思いますが、具体的にどういう役割があるのか?について説明すると
subscribeを使うことでObservableにイベントを流すことができるようになり、
後ろにonNext, onSuccess, onErrorなどをつけることができます。イメージとしては、
Observableに対してイベントを流すための手続きを行うもの
です。
実際にコードを書くときは、 ObservableType.subscribe~
のようにして書きます。
ストリームとは?
ストリームというのは、時間順に並んだ進行中のイベントのことです。イメージとしては川の流れと似てます。
上の図で、●は何かしらの型の値、✖︎はエラー、縦棒は完了を表しています。このストリームを抽出したり値を計算したりして、別のストリームを作成でき、新しく作成したストリームに対し、イベントを検知し処理を走らせるように設定できます。
(参考: https://fukatsu.tech/rxswift)
色々なObservable
次に、RxSwiftを扱う際によく出てくるObservableについて説明します。
Single
単一の要素 or 単一のエラーを返すObservable
です。
この性質によって、APIリクエストメソッドに使われることが多い
です(非同期の単一リクエスト)。
現時点でも使われることは多いと思いますが、async,awaitが出てきたことで使うモチベーションは減ってきているみたいです。
func fetch() -> Single<[Issue]> {
Single.create { observer in
let url = URL(string: "https://api.github.com/repos/rails/rails/issues")!
let task: URLSessionTask = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
do{
let result = try JSONDecoder().decode([IssueDTO].self, from: data)
observer(.success(
result.compactMap {
Issue(
number: $0.number,
title: $0.title,
body: $0.body,
url: $0.url,
user: .init(login: $0.user.login, avatarURL: $0.user.avatarURL),
updatedAt: $0.updatedAt
)
}
))
}
catch {
observer(.failure(error))
}
}
task.resume()
return Disposables.create {}
}
}
上記のサンプルコードは、APIリクエストを行い、レスポンスをIssue型の配列をSingleで返すというものになっています。
余談ですが、ここのfetchという命名は、もともとrequest()と命名していたのですが、fetchにした方が
通信を行い情報を取ってくるという意味合いが伝わりやすくなる
のでrequest()という命名と比較するとfetch()という命名の方が適切です。
DriverとSignal
Driverの特徴
として重要なものは以下の3つです。
- メインスレッドを保証
- errorなし
- 過去の値が流れる(線的に、続いているイメージ)
1について、メインスレッド保証というのはUI処理を行う際にはメインスレッドで行わなければならないというルールがあり、そのため、ViewModel内でObservableをDriverに変換し、 Viewに渡すことが多いです。
RxSwift+MVVM設計の観点で、ViewModelの役割はViewに伝えやすい形に変換すること
だから、他の層でDriverにせず、ViewModel内でDriverに変換します。
また3についてですが、過去の値というのはsubscribeした時点での値のことです。
例えば、パスワードのバリデーション(8文字以上ならokとか)で、前に入力した文字の情報が必要な場合があると思います。こういう場合は過去の値が流れるDriverを使います。
次に Signal
についてですが、Driverによく似ていて、違う点は過去の値を保持しないところです。
Signalのイメージとしては点的イメージで、ボタンタップ検知などに使われます。これはボタンタップが過去におこなわれたかどうかは不要である場合が多いからです。DriverとSignalの使い分けは、上記の内容を意識し、実装意図を伝えるためにもできるようにしたいところです。
just
単一の要素を流すことができるオペレータです。使用例としては
以下のように、文字列をViewに表示するために、bindしたい時とかに使います。
self.title = Driver.just(issue.title)
self.body = Driver.just(issue.body)
self.url = Driver.just(issue.url)
self.updatedAt = Driver.just(issue.updatedAt)
justについて追記(2022/08/31)
上記の用途で使うのはモックに固定値を入れる場合が多く、どちらかと言うとasDriver的に使う場合が多く、
onErrorなどを拾って、エラーで止まらないようにjustで代替値を与える場合に使ったりします。
SubjectとRelay
SubjectとRelayは、イベントの検知も発生も行うことができるクラス
です。
次にSubjectとRelayの使い分けについてですが、Subjectはエラーによって処理を分けたいとき
に使います。
例えば、通信処理、DB処理などでメッセージを出したい場合などです。
また、SubjectはHotなObservable
なので、Hotで扱いたい場合はSubject、Coldで使いたい場合はRelayとして扱う、といった用途の違いがあります。
SubjectとRelayの特徴について表にまとめると、次のようになります。
BehaviorRelay
ここからはRelayについて、もう少し詳しく見ていきます。BehaviorRelayというのはRelayの一種で、
- subscribeしたときに、その時点での一番新しい過去の値が取得できる
- もしも値の変更を検知したら、最新の値を取得し変換される
- 線的で、過去の値を取得できるのでDriverタイプ
という特徴があります。Behavior~ と名前がついているものはDriverと同じで、過去の値を取得できると理解しておくと覚えやすいです。
PublishRelay
PublishRelayもRelayの一種で、
- subscribeした時点での過去の値は取得できない
- 値が変更され、最新の状態が更新されて初めて値の取得ができる
- 点的で、過去の値を取得できないのでSignalタイプ
という特徴があります。Publish~ と名前がついているものはSignalと同じで、過去の値を取得できないと理解しておくと覚えやすいです。
まとめ
何か間違ってる点などありましたら、ご指摘いただけると幸いです。
最後まで読んでいただき、ありがとうございました。
追記(2022/08/31)
自分の理解が曖昧だった部分について修正しました。本当に申し訳ございません。