Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 如何通过自定义运算符将发布服务器输出转发到下游订阅服务器?_Swift_Operators_Reactive Programming_Combine - Fatal编程技术网

Swift 如何通过自定义运算符将发布服务器输出转发到下游订阅服务器?

Swift 如何通过自定义运算符将发布服务器输出转发到下游订阅服务器?,swift,operators,reactive-programming,combine,Swift,Operators,Reactive Programming,Combine,我有可能使用Combine,但我在实现细节方面遇到了很多问题。目标是提供一个发布者,该发布者将执行以下操作: 搜索缓存的值并发出该值,或: 将订阅服务器引用将发出值并将其存储在适当缓存位置的上游发布服务器 我知道这可以使用现有的运营商来完成,但如果可能的话,我想学习如何定制运营商/发布商/订阅模式。 我希望其用法类似于psedoocode的以下部分: URLSession.shared.dataTaskPublisher(for: url) .cache(with: { someSort

我有可能使用Combine,但我在实现细节方面遇到了很多问题。目标是提供一个发布者,该发布者将执行以下操作:

  • 搜索缓存的值并发出该值,或:
  • 将订阅服务器引用将发出值并将其存储在适当缓存位置的上游发布服务器
  • 我知道这可以使用现有的运营商来完成,但如果可能的话,我想学习如何定制
    运营商
    /
    发布商
    /
    订阅模式。

    我希望其用法类似于psedoocode的以下部分:

    URLSession.shared.dataTaskPublisher(for: url)
        .cache(with: { someSortOfCachingPolicy })
        .sink()
    
    为了实现这一点,我在猜测苹果在
    map
    flatMap
    方面做了什么

    我已创建了一个
    CachePublisher
    ,试图捕获上游
    发布服务器

    struct CachePublisher<Upstream: Publisher>: Publisher {
        typealias Output = Upstream.Output
        typealias Failure = Upstream.Failure
        
        var upstream: Upstream
        
        var getCache: ()->Output?
        var setCache: (Output)->Void
        
        func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
            let subscription = CachePublisherSubscription(subscriber: subscriber, upstream: upstream, getCache: getCache, setCache: setCache)
            subscriber.receive(subscription: subscription)
        }
        
        init(_ upstream: Upstream, getCache: @escaping ()->Output?, setCache: @escaping (Output)->Void) {
            self.upstream = upstream
            self.getCache = getCache
            self.setCache = setCache
        }
    }
    
    最后是一个函数,您可以将上游发布服务器传递给
    CachePublisher

    extension Publisher {
        func cache() -> CachePublisher<Self> {
            return CachePublisher(self, getCache: { nil }, setCache: { _ in })
        }
    }
    
    扩展发布程序{
    func cache()->CachePublisher{
    返回CachePublisher(self,getCache:{nil},setCache:{in})
    }
    }
    
    我不知道在所需的方法中放入什么,也不知道如何将订阅服务器向上传递到上游发布服务器。或者如何从上游发布服务器捕获值


    我脑海中浮现的想法是,下游订阅者创建了一种嵌套玩偶类型的结构,但我不知道如何将它们连接起来。

    你不需要整个
    出版商
    /
    出版商
    /
    订阅
    舞蹈,您可以自定义
    subscribe
    方法,而无需自定义类。现有联合收割机操作员可在此进行救援:)

    扩展发布程序{
    func缓存(读取:@excaping publisher.cache.read,
    write:@excaping publisher.Cache.write)->publisher.Cache{
    缓存(上游:self,读:读,写:写)
    }
    }
    扩展发布者{
    结构缓存:发布服务器{
    typealias输出=P.输出
    typealias故障=P.故障
    typealias读取=()->输出?
    typealias Write=(输出)->Void
    私人出租上游:P
    私人阅读:阅读
    私人让写:写
    初始化(上游:P,读:@转义读,写:@转义写){
    self.上游=上游
    self.read=read
    self.write=write
    }
    func receive(订阅服务器:S),其中S:subscriber,Self.Failure==S.Failure,Self.Output==S.Input{
    如果让cachedValue=read(){
    Just(cachedValue).setFailureType(to:Failure.self).receive(订阅者:订阅者)
    }否则{
    upstream.handleEvents(receiveOutput:write).receive(订户:订户)
    }
    }
    }
    }
    

    handleEvents
    在某种程度上打破了在编写自定义操作符管道时建议遵循的“纯函数”范式,但是由于您无论如何都需要写入缓存,这已经是一个副作用,调用
    handleEvents
    的附加影响并没有那么大。

    使自定义
    订阅也成为
    订户
    允许它连接两个方向。当缓存getter生成结果时,它被发送到下游订阅服务器。但是,当getter没有发送请求时,请求被转发到上游发布服务器,该发布服务器将发出一个值

    然后,该值由自定义订阅的订阅方方法捕获,并转发给下游订阅方

    extension CachePublisher {
        class CachePublisherSubscription<Downstream: Subscriber>: Subscription, Subscriber where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure {
            typealias Input = Upstream.Output
            typealias Failure = Upstream.Failure
            
            var downstream: Downstream
            var upstream: Upstream
            var upstreamSubscription: Subscription?
            
            var read: Read
            var write: Write
                    
            init(downstream: Downstream, upstream: Upstream, read: @escaping Read, write: @escaping Write) {
                self.downstream = downstream
                self.upstream = upstream
                self.read = read
                self.write = write
                
                upstream.subscribe(self)
            }
            
            func request(_ demand: Subscribers.Demand) {
                if let cachedValue = read() {
                    downstream.receive(cachedValue)
                } else {
                    upstreamSubscription?.request(demand)
                }
            }
            
            // keep a reference to the upstream subscription
            func receive(subscription: Subscription) {
                self.upstreamSubscription = subscription
            }
            // pass input downstream
            func receive(_ input: Input) -> Subscribers.Demand {
                self.write(input)
                return downstream.receive(input)
            }
            // pass completion downstream
            func receive(completion: Subscribers.Completion<Failure>) {
                downstream.receive(completion: completion)
            }
            
            func cancel() {
                //TO-DO: Finish cancellation
            }
        }
    }
    
    扩展缓存发布器{ 类CachePublisherSubscription:Subscription,订阅服务器,其中下游.Input==上游.Output,下游.Failure==上游.Failure{ typealias输入=上游。输出 typealias Failure=上游。失败 下游:下游 上游:上游 var上游订阅:订阅? 变量读取:读取 变量写入:写入 初始化(下游:下游,上游:上游,读:@转义读,写:@转义写){ self.down=下游 self.上游=上游 self.read=read self.write=write 上游。订阅(自我) } func请求(uDemand:Subscribers.demand){ 如果让cachedValue=read(){ 接收(缓存值) }否则{ 上游订阅?请求(需求) } } //保留对上游订阅的引用 func接收(订阅:订阅){ self.upstreamSubscription=订阅 } //向下游传递输入 func receive(u-input:input)->Subscribers.Demand{ 自写(输入) 返回下游。接收(输入) } //下游通过竣工 func接收(完成:订阅者。完成){ 下游。接收(完成:完成) } func cancel(){ //待办事项:完成取消 } } }
    Gotcha。这是一个很好的步骤,我将使用它进行一些探索,但是有没有一种方法可以用“纯函数”的方式来实现呢?我之所以要求这样做,是因为我认为这有助于学习制作其他纯函数解决方案。@promacuser好吧,我认为如果你还需要更新缓存,就不能有一个干净的管道,因为这会产生副作用,并会破坏“纯度”我做了更多的阅读,我想我要做的是,订阅也必须是一个订户。我要写另一个答案
    extension CachePublisher {
        class CachePublisherSubscription<Downstream: Subscriber>: Subscription, Subscriber where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure {
            typealias Input = Upstream.Output
            typealias Failure = Upstream.Failure
            
            var downstream: Downstream
            var upstream: Upstream
            var upstreamSubscription: Subscription?
            
            var read: Read
            var write: Write
                    
            init(downstream: Downstream, upstream: Upstream, read: @escaping Read, write: @escaping Write) {
                self.downstream = downstream
                self.upstream = upstream
                self.read = read
                self.write = write
                
                upstream.subscribe(self)
            }
            
            func request(_ demand: Subscribers.Demand) {
                if let cachedValue = read() {
                    downstream.receive(cachedValue)
                } else {
                    upstreamSubscription?.request(demand)
                }
            }
            
            // keep a reference to the upstream subscription
            func receive(subscription: Subscription) {
                self.upstreamSubscription = subscription
            }
            // pass input downstream
            func receive(_ input: Input) -> Subscribers.Demand {
                self.write(input)
                return downstream.receive(input)
            }
            // pass completion downstream
            func receive(completion: Subscribers.Completion<Failure>) {
                downstream.receive(completion: completion)
            }
            
            func cancel() {
                //TO-DO: Finish cancellation
            }
        }
    }