はじめに
Swift の Combine フレームワークは、リアクティブプログラミングを実現するためのツールです。その中の PassthroughSubject
は、イベントを外部から送信し、それを購読者が受け取る仕組みを提供します。
PassthroughSubject とは?
PassthroughSubject
は、イベントが発生した際にそのデータを即座に購読者へ通知するための Combine の仕組みの一つです。状態を保持しないため、新しい購読者は過去のデータを受け取ることはできません。
CurrentValueSubject
との違い
PassthroughSubject | CurrentValueSubject | |
---|---|---|
データの保持 | なし | 最新の値を保持 |
過去のデータの取得 | 不可 | 可能 |
初期値 | 必要なし | 必要 |
基本的な使い方
1. PassthroughSubject
の定義
まずは PassthroughSubject
を定義します。
import Combine
class EventManager {
var subject = PassthroughSubject<String, Never>()
}
ここで PassthroughSubject<String, Never>
は、String
型のデータを送信し、エラーは発生しない (Never
) ことを示しています。
2. 購読者の追加
購読者(sink
)を使って、データを受け取る処理を実装します。
let manager = EventManager()
let cancellable = manager.subject.sink { value in
print("受信: \(value)")
}
3. データを送信する
購読者を追加した後に、send
メソッドを使ってデータを送信します。
manager.subject.send("イベント発生!")
// コンソール出力: 受信: イベント発生!
購読者が存在する場合、このデータは即座に送信され、sink
内の処理が実行されます。
4. 購読のキャンセル
購読を続けると、メモリリークの原因になる場合があります。そのため、AnyCancellable
を使って購読を管理し、適切なタイミングでキャンセルするのが望ましいです。
class EventManager {
var subject = PassthroughSubject<String, Never>()
private var cancellables = Set<AnyCancellable>()
init() {
subject
.sink { value in
print("受信: \(value)")
}
.store(in: &cancellables)
}
}
これにより、EventManager
のインスタンスが解放された際に購読も自動的にキャンセルされます。
簡単なユースケース
1. ボタンのクリックイベントを通知
SwiftUI では、ボタンのクリックを PassthroughSubject
を使って通知できます。
import SwiftUI
import Combine
class ButtonViewModel: ObservableObject {
var buttonTapped = PassthroughSubject<Void, Never>()
}
struct ContentView: View {
@StateObject private var viewModel = ButtonViewModel()
var body: some View {
Button("タップ") {
viewModel.buttonTapped.send()
}
.onAppear {
viewModel.buttonTapped.sink {
print("ボタンがタップされました!")
}
}
}
}
2. API リクエストのステータスを通知
PassthroughSubject
を利用して、API リクエストの状態を購読できます。
class APIService {
var responseSubject = PassthroughSubject<String, Never>()
func fetchData() {
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
self.responseSubject.send("データ取得完了")
}
}
}
let apiService = APIService()
let cancellable = apiService.responseSubject.sink { response in
print(response)
}
apiService.fetchData()