Publisher
タップからデシベルとタイムスタンプを送信するカスタムCombine AVAudioEngine
を記述しようとしています。多数のチュートリアルとWWDCビデオを実行した後、私はstillが、購読しているPublisher
sをSubscriber
が追跡する方法の例を見つけることができません。
public typealias AudioVolume = Double
public struct AudioVolumePublisher: Publisher {
public typealias Output = AudioVolume
public typealias Failure = Error
}
public class AudioVolumeSubscription<S: Subscriber>: NSObject, Subscription {
private var subscriber: S?
public var combineIdentifier = CombineIdentifier()
public init(for subscriber: S) {
self.subscriber = subscriber
}
public func request(_ demand: Subscribers.Demand) {
...
}
public func cancel() {
subscriber = nil
}
}
AudioVolumePublisher
はアクティブなサブスクライバーのリストを格納する必要があると想定していますが、次のようなプロパティを追加しています
var subscribers = [S]()
Subscriber
には型が関連付けられているため、コンパイルされません。これはSubscriber
sを処理するための適切なアプローチでもありますか?そうであれば、それらを格納するための最良の方法は何ですか?タイプ消去は私の唯一の実用的なオプションですか?
基本的に、受信したサブスクライバーを何かにラップする必要があります。これについて独自の抽象化を作成することもできますが、Combineはすでに必要なツールを提供しています:AnySubscriber(タイプ消しゴム)。
public struct AudioVolumePublisher: Publisher {
public typealias Output = AudioVolume
public typealias Failure = Error
private var subscriptions = [AudioVolumeSubscription]()
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
// some cleanup first, to remove disconnected subscribers
subscribers = subscribers.filter { $0.subscriber != nil }
// register the subscriber
subscribers.append(AudioVolumeSubscription(subscriber))
// signal availability to the subscriber
subscriber.receive(subscription)
}
この実装では、AudioVolumeSubscription
はジェネリックではないことに注意してください。
public class AudioVolumeSubscription: NSObject, Subscription {
// also note the weak reference, we don't want to keep the subscriber alive more
// than needed, and this also avoids possible retain cycles
private weak var subscriber: AnySubscriber<AudioVolume, Error>?
// the generic responsibility was moved on the shoulders of the initializer
public init<S: Subscriber>(for subscriber: S) where S.Input == AudioVolume, S.Failure == Error {
self.subscriber = AnySubscriber(subscriber)
}
また、これは単純な実装であり、パブリッシャーはすぐに可用性を通知します。すべてのパブリッシャーがこれを行うわけではありませんが、あなたの場合はそうです。