RxSwiftとは?導入方法と使い方まとめ!ストリームを理解する

この記事からわかること
- RxSwiftとは?
- ReactiveXの特徴とメリット
- 初学者がRxSwiftを理解するために重要なポイント
- ストリームやObservableなどの意味とは?
- Operatorの種類
- mapやfilterの使い方
index
[open]
\ アプリをリリースしました /
この記事はRxSwiftのことを何も知らない状態から理解するための備忘録として作成します。至らぬ点や間違っている点がございましたら教えていただけると嬉しいです。また参考にさせていただいた記事は全て一番最後に記載しております。
重要そうな単語など
まずはいろいろな情報を見た結果、重要そうな単語などを整理しておきます。
- ReactiveX
- ストリーム
- シーケンス
- Observable
- オブザーバーパターン
- 非同期
- イベント
- スレッド
- 宣言的(Declarative Programming)
- Functional Reactive Programming(関数型リアクティブプログラミング)
なんとなく単語を見ていると雰囲気は掴めそうですがなかなか難しそうです。
RxSwiftとは?
そもそもRxSwiftとはMicrosoftが2011年にリリースしたReactiveX(Reactive Extensions)と呼ばれるライブラリのSwift版です。ReactiveXはもともと.NET Framework用に開発されたライブラリで、その後SwiftだけでなくC++やJava、JavaScriptなど数多くのプラットフォームにも提供されるようになりました。
ReactiveXの概要
ReactiveXの公式サイトによる説明は以下のようになっています。
”ReactiveXは、監視可能なシーケンスを使用して、非同期およびイベントベースのプログラムを作成するためのライブラリです。
オブザーバーパターンを拡張してデータやイベントのシーケンスをサポートし、低レベルのスレッド化、同期化、スレッド セーフ、並行データ構造、および非I/O をブロックします。”
要約すると非同期処理やイベント処理、時間経過に関数処理を1つのシーケンス(順序)として操作、観測できる機能を提供するAPIということでしょうか。
またReactiveXは「リアクティブプログラミング」と呼ばれるプログラミング手法を取り入れていると言われています。リアクティブプログラミングの良い例がExcelで「値を入力すると自動で計算結果が変更される」のがポイントになるようです。
導入するメリット
RxSwiftを導入することの大きなメリットはやはり非同期処理やイベント処理などを(シーケンスとして)簡潔に書けるようになることです。 非同期処理を記述する際にコールバック(completionHandler)が増えすぎて可読性が下がったり、APIの結果で処理を分岐したりといった複雑になりがちなコードをスッキリ書けるようになります。
おすすめ記事:【Swift】completionHandlerとは?使い方と@escapingの意味
他にも時間の流れに関する処理が得意だったり、コード自体にまとまりが生まれ、かつ一貫した流れで記述できるので全体の概要が掴みやすくなるのもメリットの1つです。
ストリーム(Stream)とシーケンス(Sequence)とは?
ReactiveXを理解するにはストリーム(stream)を理解する必要がありそうです。そもそもストリームとは日本語で「流れ」を意味する英単語であり、プログラミングの分野ではデータの入出力の流れを保持する抽象的なオブジェクトのことを指します。
ReactiveXでもストリームが登場し、時間の流れの中に「データの変更から完了またはエラーまで」を一連の順序(シーケンス)として保持しています。
その一連の流れはマーブルダイアグラムとして表現されます。左から右に行くにつれ時間の経過を表し、その過程の中でデータの変化などのイベントを検知、最終的には完了またはエラーでストリームは終了します。

そしてこの考え方こそがリアクティブプログラミングのようです。
Observable
Observableとは日本語で「観測可能」を意味する英単語です。ReactiveXではストリームを観測、検知可能なクラスのことをObservableと呼び、変化やエラー、完了を検知すると通知してくれます。onNext
はイベントの発生のたびに複数回検知される可能性があり、onError
またはonCompleted
はストリームの中でどちらか1回しか観測されません。そしてそのどちらかを検知したタイミングまたは明示的に指定したタイミングで監視は終了します。
種類 | 概要 |
---|---|
onNext | イベントを検知 |
onError | エラーの発生を検知 |
onCompleted | 完了を検知 |
RxSwiftではObservableクラスとして用意されています。
おすすめ記事:【RxSwift】Observableクラスとは?subscribeメソッドの使い方!
オブザーバーパターン
”「オブジェクトがクラスに依存せずに他のオブジェクトに状態変化を通知するにはどうすればよいか」。この問題をスマートに解決するために考えられたデザインパターンがオブザーバーパターンです。
オブザーバーパターンは監視対象 (サブジェクト/プロバイダー) と観測者 (オブザーバー/リスナー) から成ります。「監視対象のオブジェクトを観測者オブジェクトが監視していて、監視対象が変化したのを観測者が確認したら特定のアクションを起こす」の考え方が基本です。”
オブザーバーパターンとは「状態変化などを観測し、通知する」タイプのデザインパターンの一種です。対象となる2つの役割があり「観測される側(Subject)」と「観測する側(Observer)」に分かれます。(Subjectは被写体といった意味です)
おすすめ記事:【GoF】Observerパターンとは?Publish-Subscribeパターンとの違い
ここまでのまとめ
- ReactiveX:非同期処理やイベント処理に長けたライブラリ
- ストリーム:扱うデータの流れそのもの
- シーケンス:順序のこと
- リアクティブプログラミング:Excelのような値と振る舞いが連動するプログラミング手法
- Observable:使用する観測可能なクラス
- オブザーバーパターン:観測→通知のデザインパターン
宣言的(Declarative programming)
宣言的と聞くとSwift UIを思い出します。プログラミングには「Declarative Programming(宣言型プログラミング)」と「Imperative Programming(命令型プログラミング)」があります。
- 宣言型プログラミング:何を処理するかを記述
- 命令型プログラミング:どのように処理するか記述
「What(何を)」と「How(どのように)」で区別することが多いですがSwift UIとUIKitの2つを見てみると分かりやすいです。
Swift UIでは「こんなボタンを作りたい」と書くだけでボタンが出来上がります。
import SwiftUI
struct TestView: View {
@State var check = true
var body: some View {
Button(action: {
check.toggle()
}){
Text(check ? "ON" : "OFF")
}
}
}
一方UIKitでは「ボタンを構築して、位置を決めて、タイトルを決めて、アクションを定義して紐づけて...」と1行ずつ処理していく形になります。
import UIKit
class ViewController: UIViewController {
let button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
let screenWidth = self.view.frame.width
let screenHeight = self.view.frame.height
// 位置とサイズ
button.frame = CGRect(x:screenWidth/4, y:screenHeight/2,
width:screenWidth/2, height:60)
// タイトル
button.setTitle("ボタン", for:UIControl.State.normal)
// タップされたときの処理
button.addTarget(self,
action: #selector(ViewController.buttonTapped(sender:)),
for: .touchUpInside)
// Viewにボタンを追加
self.view.addSubview(button)
}
@objc func buttonTapped(sender : Any) {
print("ボタンをタップされたよ")
}
}
そしてReactiveXでは宣言的にコードを記述できるようになっているようです。
RxSwiftの導入方法
おすすめ記事:【Swift UI】CocoaPodsのインストール方法と使い方!
RxSwiftを使用するにはライブラリの導入が必要です。今回はCocoa Podsを使ってインストールしていくので「PodFile」に以下の2行を書き込みインストールします。
pod 'RxSwift'
pod 'RxCocoa'
$ pod install
Analyzing dependencies
Downloading dependencies
Installing RxCocoa (6.5.0)
Installing RxRelay (6.5.0)
Installing RxSwift (6.5.0)
Generating Pods project
Integrating client project
RxCocoa
RxCocoaはRxSwiftの拡張ライブラリでUIKitのビュークラスをRxが使えるようにするためのライブラリです。
これでプロジェクト内でRxSwiftが使えるようになりました。ここからは使用方法を見てみます。
記述方法
RxSwiftでは基本的にメソッドチェーン方式で記述していきます。Swift UIを使っている人なら理解しやすい記述方式だと思います。
なので用意されているメソッドには何かしらの返り値があることが多いです。その返り値に対してさらにメソッドを呼び出し、さらに返り値から…となります。
ここで実際のコードを少しみてみます。
let text = textField.rx.text
text.subscribe(onNext: { _ in
print("next")
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
Observableを観測するためにはsubscribe
(購読)メソッドを記述します。このメソッドによりストリームの観測が開始され、その各引数にクロージャーを渡すことで任意の通知が届いた時に処理を行わせることが可能になってます。
データのバインディング

まずは簡単にUITextFieldの値をラベルにバインディングしてリアクティブに表示させてみます。使用するには上部にimport文を忘れないように記述します。UIKitのビューに対して使用していくのでRxCocoaも必要になります。(そのままコピペで動作します。)
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.frame = CGRect(x:130, y:200,width:200, height:50)
label.text = "placeholder..." // textField.text とBindingされるので表示されることはない
self.view.addSubview(label)
let textField = UITextField()
textField.frame = CGRect(x:130, y:300,width:200, height:50)
textField.borderStyle = UITextField.BorderStyle.line
self.view.addSubview(textField)
// Observableクラスの取得
let text = textField.rx.text
// ストリームの観測開始
text.subscribe(onNext: { [weak self] text in
label.text = text // 変化した値をUIとリンク
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
}
}
ここでポイントになるのはtextField
から呼び出しているプロパティやメソッドです。
rxプロパティ:Reactive<UITextField>を取得
textプロパティ:ControlProperty<<Optional<String>>を取得
subscribeメソッド:購読
disposed:観測終了用
このコードをマーブルダイアグラムで表すと以下のような感じでしょうか?

イベントの発生はUITextField
のtext
プロパティが変化したタイミングです。そのイベントがObserverに通知され処理(labelのtextに格納)が実行されます。
bind
データをバインディングするためにbind
メソッドを使用するともっと簡潔に記述することが可能です。以下もsubscribeを使ったコードと同じ動きをします。
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.frame = CGRect(x:130, y:200,width:200, height:50)
label.text = "placeholder..."
self.view.addSubview(label)
let textField = UITextField()
textField.frame = CGRect(x:130, y:300,width:200, height:50)
textField.borderStyle = UITextField.BorderStyle.line
self.view.addSubview(textField)
textField.rx.text.orEmpty
.bind(to: label.rx.text)
.disposed(by: disposeBag)
}
データバインディングと聞くと双方向を想像しますが、RxSwiftのデータバインディングは双方向ではなく単方向のようです。
おすすめ記事:【RxSwift】DisposeBagクラスの使い方!Disposable
タップイベントを観測する
続いてボタンのタップイベントを観測してみます。基本的な記述方法は先ほどと同じです。以下のコードでボタンをタップするごとにonNext
が実行されます。
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.frame = CGRect(x: 0, y:0, width: 100 , height: 40)
button.setTitle("Tap!!", for: .normal)
button.backgroundColor = UIColor.orange
button.center = self.view.center
self.view.addSubview(button)
button.rx.tap
.subscribe(onNext: { _ in
print("tap")
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
}
循環参照
subscribe
メソッドのクロージャー内ではselfを強参照してしまい循環参照が発生する可能性があるのでweak
またはunowned
を付与して弱参照になるようにしておく。
text.subscribe(onNext: { [weak self] text in
label.text = text // 変化した値をUIとリンク
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
おすすめ記事:Swift】weakの役割とは?循環参照の意味とARCについて
Operator
RxSwiftではさらにOperatorという概念があります。これは生成されているObservableのイベントを編集や絞り込みを行った新しいObservableを生成する役割を持っています。それ以外にも複数のObservableを結合することも可能です。
Operatorにはたくさん種類があるので使用頻度の高そうなものをいくつか紹介します。
map
これは配列などに使用するmap関数と同じような役割を持ったOperatorです。map関数は配列内の全ての要素に対して任意の処理を実行できる関数でした。ここでのmapも同様に全てのイベントに対して任意の処理(クロージャー)を実行させる役割を持っています。
おすすめ記事:【Swift】map関数の使い方!flatMap/compactMapとの違いと使い方
例えば以下は入力された値がイベントとして渡されるのをUITextFieldの値:\(入力値)
という形式に編集して渡しています。
let text = textField.rx.text
text.map{ [weak self] text -> String? in
guard let text = text else { return nil }
return "UITextFieldの値:\(text)"
}
.subscribe(onNext: { [weak self] text in
label.text = text
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
マーブルダイアグラムにすると以下のようなイメージでしょうか?

記述方法
public func map<Result>(_ transform: @escaping (Element) throws -> Result)
-> Observable<Result> {
Map(source: self.asObservable(), transform: transform)
}
一応mapの記述方法も見ておきます。定義は上記のようになっており引数にはクロージャーを渡します。引数のクロージャー内ではイベント発生後に渡される値が受け取れます。
今回はUITextFieldのtextプロパティの値を使用して編集した文字列を送るので受け取るための変数と返り値の型を明示的に定義「{ text -> String? in ...}
」しています。
おすすめ記事:【Swift】クロージャとは?関数との違いとキャプチャの意味
text.map{ [weak self] text -> String? in
guard let text = text else { return nil }
return "UITextFieldの値:\(text)"
}
filter
filter
も配列などに使用する関数と同じ役割のOperatorです。filterを使用することで条件にマッチしたイベントのみにフィルタリングを実装することができます。
おすすめ記事:filterメソッドの使い方
text.filter{ $0!.count > 5 }
.subscribe(onNext: { text in
label.text = text
}, onError: { _ in
print("error")
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)

参考文献:公式リファレンス:ReactiveX
参考文献:GitHub:ReactiveX
参考文献:【Swift】RxSwift入門 ①
参考文献:Rx入門 (1) - はじめに
参考文献:RxSwiftについてようやく理解できてきたのでまとめることにした(1)
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。