Swift Combine: Buffer upstream values and emit them at a steady rate?

后端 未结 4 940
被撕碎了的回忆
被撕碎了的回忆 2021-02-06 04:16

Using the new Combine framework in iOS 13.

Suppose I have an upstream publisher sending values at a highly irregular rate - sometimes seconds or minutes may go by withou

4条回答
  •  礼貌的吻别
    2021-02-06 04:50

    Just wanted to mention that I adapted Rob's answer from earlier and converted it to a custom Publisher, in order to allow for a single unbroken pipeline (see comments below his solution). My adaptation is below, but all the credit still goes to him. It also still makes use of Rob's step operator and SteppingSubscriber, as this custom Publisher uses those internally.

    Edit: updated with buffer as part of the modulated operator, otherwise that would be required to be attached to buffer the upstream events.

    public extension Publisher {
        func modulated(_ pace: Context.SchedulerTimeType.Stride, scheduler: Context) -> AnyPublisher {
            let upstream = buffer(size: 1000, prefetch: .byRequest, whenFull: .dropNewest).eraseToAnyPublisher()
            return PacePublisher(pace: pace, scheduler: scheduler, source: upstream).eraseToAnyPublisher()
        }
    }
    
    final class PacePublisher: Publisher {
        typealias Output = Source.Output
        typealias Failure = Source.Failure
    
        let subject: PassthroughSubject
        let scheduler: Context
        let pace: Context.SchedulerTimeType.Stride
    
        lazy var internalSubscriber: SteppingSubscriber = SteppingSubscriber(stepper: stepper)
        lazy var stepper: ((SteppingSubscriber.Event) -> ()) = {
            switch $0 {
            case .input(let input, let promise):
                // Send the input from upstream now.
                self.subject.send(input)
    
                // Wait for the pace interval to elapse before requesting the
                // next input from upstream.
                self.scheduler.schedule(after: self.scheduler.now.advanced(by: self.pace)) {
                    promise(.more)
                }
    
            case .completion(let completion):
                self.subject.send(completion: completion)
            }
        }
    
        init(pace: Context.SchedulerTimeType.Stride, scheduler: Context, source: Source) {
            self.scheduler = scheduler
            self.pace = pace
            self.subject = PassthroughSubject()
    
            source.subscribe(internalSubscriber)
        }
    
        public func receive(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
            subject.subscribe(subscriber)
            subject.send(subscription: PaceSubscription(subscriber: subscriber))
        }
    }
    
    public class PaceSubscription: Subscription {
        private var subscriber: S?
    
        init(subscriber: S) {
            self.subscriber = subscriber
        }
    
        public func request(_ demand: Subscribers.Demand) {
    
        }
    
        public func cancel() {
            subscriber = nil
        }
    }
    

提交回复
热议问题