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