概要 結合フレームワークでは、パブリッシャーは プロトコルに準拠する型です。サブスクライバーに値のストリームを提供する責任があります。 プロトコルは、2 つの関連する型を定義します。 と です。これらは、それぞれ発行者が出力できる値の型とスローできるエラーの型を示します。 Publisher Publisher Output Failure パブリッシャーは、時間の経過とともに 1 つ以上の値を発行でき、完了または失敗することもあります。サブスクライバーがパブリッシャーにサブスクライブすると、パブリッシャーはサブスクライバーの メソッドを呼び出して、サブスクライバーが値の流れを制御するために使用できる オブジェクトを渡します。サブスクライバーは、パブリッシャーで メソッドを呼び出して、新しい値を受け取ることもできます。 receive(subscription:) Subscription receive(_:) Combine フレームワークには、 、 、 、 、 など、さまざまな種類のパブリッシャーを作成するために使用できる組み込みパブリッシャーが多数用意されています。さらに、 プロトコルに準拠し、必要なメソッドを実装することで、独自のカスタム パブリッシャーを作成できます。 Just Fail Empty Deferred Sequence Publisher パブリッシャーを組み合わせて、より複雑なパイプラインを作成することもできます。 Combine フレームワークは、 、 、 、 、 、および など、パブリッシャーを変更および結合するために使用できる多数の組み込み演算子を提供します。これらの演算子は プロトコル拡張によって提供され、任意の発行元で呼び出すことができます。 map filter reduce flatMap zip merge Publisher ここで、私がプロジェクトで使用している便利なパブリッシャーをいくつか紹介したいと思います。 繰り返しタイマー パブリッシャー Swift でカスタム間隔で繰り返しタイマーを使用するパブリッシャーを実装するには、Foundation フレームワークの クラスを使用できます。これを行う方法の例を次に示します。 Timer は プロトコルに準拠しています。 RepeatingTimeSubscription Subscription private class RepeatingTimerSubscription<S: Subscriber>: Subscription where S.Input == Void { private let interval: TimeInterval private let queue: DispatchQueue private var subscriber: S? private var timer: Timer? init(interval: TimeInterval, queue: DispatchQueue, subscriber: S) { self.interval = interval self.queue = queue self.subscriber = subscriber } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.queue.async { _ = self?.subscriber?.receive() } } } func cancel() { timer?.invalidate() timer = nil subscriber = nil } } は プロトコルに準拠しています。 RepeatingTimePublisher Publisher import Foundation import Combine final class RepeatingTimerPublisher: Publisher { typealias Output = Void typealias Failure = Never private let interval: TimeInterval private let queue: DispatchQueue init(interval: TimeInterval, queue: DispatchQueue = .main) { self.interval = interval self.queue = queue } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = RepeatingTimerSubscription( interval: interval, queue: queue, subscriber: subscriber ) subscriber.receive(subscription: subscription) } } このパブリッシャーを使用するには、そのインスタンスを作成し、 プロトコルの メソッドを使用してサブスクライブできます。 Publisher sink 例えば: private var cancellable: AnyCancellable? func subscribeOnTimer(interval: TimeInterval) { let publisher = RepeatingTimerPublisher(interval: interval) cancellable = publisher.sink { print("Timer fired!") } } //TEST THE METHOD subscribeOnTimer(interval: 5.0) これにより、「タイマーが作動しました!」と出力されます。 5秒ごと。 メソッドによって返される オブジェクトで メソッドを呼び出すことにより、サブスクリプションをキャンセルできます。 sink AnyCancellable cancel 例えば: deinit { cancellable?.cancel() } ロングポーリングパブリッシャー Swift で結合フレームワークを使用してロング ポーリングを実装するには、指定された間隔でネットワーク リクエストを行い、応答を出力として返すパブリッシャーを作成できます。これを行う方法の例を次に示します。 失敗したケースのカスタム エラー列挙。 enum CustomError: Error { case invalidResponse case invalidDecoding case error } プロトコルに準拠しています。 LongPollingSubscription Subscription private class LongPollingSubscription<S: Subscriber, Output: Decodable>: Subscription where S.Input == Output, S.Failure == CustomError { private let url: URL private let interval: TimeInterval private let decoder: JSONDecoder private var subscriber: S? private var timer: Timer? private var task: URLSessionDataTask? init( url: URL, interval: TimeInterval, subscriber: S, decoder: JSONDecoder = JSONDecoder() ) { self.url = url self.interval = interval self.subscriber = subscriber self.decoder = decoder } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.makeRequest() } makeRequest() } func cancel() { timer?.invalidate() timer = nil task?.cancel() task = nil subscriber = nil } private func makeRequest() { task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self else { return } if let error = error as? S.Failure { self.subscriber?.receive( completion: .failure(error) ) return } guard let data else { self.subscriber?.receive( completion: .failure(.invalidResponse) ) return } do { let output = try self.decoder.decode( Output.self, from: data ) _ = self.subscriber?.receive(output) } catch { self.subscriber?.receive( completion: .failure(.invalidDecoding) ) } } task?.resume() } } プロトコルに準拠しています。 LongPollingPublisher Publisher final class LongPollingPublisher<Output: Decodable>: Publisher { typealias Failure = CustomError private let url: URL private let interval: TimeInterval init(url: URL, interval: TimeInterval) { self.url = url self.interval = interval } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = LongPollingSubscription( url: url, interval: interval, subscriber: subscriber ) subscriber.receive(subscription: subscription) } } は、 プロトコルに準拠する任意の汎用タイプの応答を使用できることを意味します。 <Output: Decodable> Decodable テストのために、 に準拠するモデルを作成する必要があります。 の公開 API を使用しています。 Decodable https://pixabay.com/api それを PhotoResponse 構造体にしましょう: struct PhotoResponse: Decodable { struct Photo: Decodable { let user: String let id: Int let largeImageURL: String } let hits: [Photo] let total: Int } このパブリッシャーを使用するには、そのインスタンスを作成し、 プロトコルの メソッドを使用してサブスクライブできます。例えば: Publisher sink private var cancellable: AnyCancellable? private func pollingTest() { let url = URL(string: "https://pixabay.com/api/?key={your_key}")! let publisher = LongPollingPublisher<PhotoResponse>( url: url, interval: 5.0 ) cancellable = publisher.sink(receiveCompletion: { completion in switch completion { case .finished: print("Completed") case .failure(let error): print("Error: \(error)") } }, receiveValue: { response in print("Received response: \(response)") }) } //TEST THE METHOD pollingTest() もう一つ Swift の Combine フレームワークを使用して作成できる便利なカスタム パブリッシャーが多数あります。以下にいくつかの例を示します。 : 指定された通知が に投稿されたときに値を発行するパブリッシャー。このパブリッシャーを使用して、デバイスのローテーションやネットワーク ステータスの変化など、システムまたはアプリ固有のイベントに対応できます。 NotificationCenterPublisher NotificationCenter : キーボードが表示または非表示になったときに値を発行するパブリッシャー。このパブリッシャーを使用して、キーボードが表示または非表示になったときのビューのレイアウトを調整できます。 KeyboardPublisher : ユーザーの場所が変更されたときに値を発行するパブリッシャー。このパブリッシャーを使用して、ユーザーの場所を追跡し、場所に基づいてアクションを実行できます。 CoreLocationPublisher : UIButton や UITextField などの で指定されたイベントが発生したときに値を発行するパブリッシャー。これは、リアクティブな方法でユーザー インタラクションを処理するために使用できます。 UIControlEventPublisher UIControl これらは、Combine フレームワークを使用して作成できるカスタム パブリッシャーのタイプのほんの一例です。重要なのは、アプリケーションの要件を理解し、フレームワークによって提供される利用可能なビルディング ブロックを使用して、それらの要件を満たすパブリッシャーを作成することです。 結論は 最後に、Combine フレームワークが関数型リアクティブ プログラミング パラダイムを使用していることに注意することが重要です。これは、時間の経過に伴うイベント ストリームを処理するプログラミング モデルです。パブリッシャーは、サブスクライバーやオペレーターと共に、このパラダイムのコア ビルディング ブロックであり、複雑で応答性の高いアプリケーションの作成を容易にします。