首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >快速组合:如何创建可重用的Publishers.Map以连接到多个上游发布服务器?

快速组合:如何创建可重用的Publishers.Map以连接到多个上游发布服务器?
EN

Stack Overflow用户
提问于 2020-04-28 02:17:38
回答 1查看 1.6K关注 0票数 0

我试图把我的头脑围绕着合并,当我重构代码时,当我试图重新构建以避免重复自己的时候,我会遇到一些困惑。

在这种情况下,我想随时更新一个值:

  • A特定主题更改
  • 应用程序到前台
  • 3秒钟刷新计时器触发

由于3秒刷新计时器在第一次启动之前不会发布任何内容,所以我假设我需要多个发布者。

我总是只使用来自主题的值,而忽略从前台通知和计时器发送的任何值。

下面是一些示例代码,其中我只基于一个发布者来处理值:

代码语言:javascript
复制
import UIKit
import Combine

class DataStore {

    @Published var fillPercent: CGFloat = 0

    private var cancellables: [AnyCancellable] = []

    // how much the glass can hold, in milliliters
    private let glassCapacity: Double = 500

    // how full the glass is, in milliliters
    private var glassFillLevelSubject = CurrentValueSubject<Double,Never>(250)

    // a publisher that fires every three seconds
    private let threeSecondTimer = Timer
        .publish(every: 3,
                 on: RunLoop.main,
                 in: .common)
        .autoconnect()

    // a publisher that fires every time the app enters the foreground
    private let willEnterForegroundPublisher = NotificationCenter.default
        .publisher(for: UIApplication.willEnterForegroundNotification)

    init() {
        // publisher that fires any time the glass level changes or three second timer fires
        let glassLevelOrTimerPublisher = Publishers.CombineLatest(glassFillLevelSubject, threeSecondTimer)
            // is there shorthand to only return the first item? like .map{ $0 }?
            .map { glassFillLevel, timer -> Double in
                return glassFillLevel
            }
            .eraseToAnyPublisher()

        // publisher that fires any time the glass level changes or three second timer fires
        let glassLevelOrForegroundPublisher = Publishers.CombineLatest(glassFillLevelSubject, willEnterForegroundPublisher)
            .map{ glassFillLevel, notification -> Double in
                return glassFillLevel
            }
            .eraseToAnyPublisher()

        // how can I define map and everything after it as something, and then subscribe it to the two publishers above?
        glassLevelOrTimerPublisher
            .map{ fillLevelInMilliliters in

                let fillPercent = fillLevelInMilliliters / self.glassCapacity

                return CGFloat(fillPercent)
            }
            .assign(to: \.fillPercent, on: self)
            .store(in: &cancellables)
    }
}

我想我在这里要做的是将.map和它之后的所有内容分离开来,并以某种方式将其订阅给上述两家出版商。

我尝试过这样做,作为一种在.map之后隔离所有东西以使其可重用的方法:

代码语言:javascript
复制
    let fillPercentStream = Publishers.Map{ fillLevelInMilliliters in

        let fillPercent = fillLevelInMilliliters / self.glassCapacity

            return CGFloat(fillPercent)
        }
        .assign(to: \.fillPercent, on: self)
        .store(in: &cancellables)

但是这给了我一个错误,上面写着Missing argument for parameter 'upstream' in call,所以我尝试为这个参数添加一些内容,最后得到如下结果:

代码语言:javascript
复制
    let fillPercentStream = Publishers.Map(upstream: AnyPublisher<Double,Never>, transform: { fillLevelInMilliliters in

            let fillPercent = fillLevelInMilliliters / self.glassCapacity

            return CGFloat(fillPercent)
        })
        .assign(to: \.fillPercent, on: self)
        .store(in: &cancellables)

然后,我最终出现了一个编译器错误链:Unable to infer complex closure return type; add explicit type to disambiguate,它建议我在.map中指定-> CGFloat,这是我添加的,但是它告诉我应该将CGFloat更改为_,最后会出现更多的错误。

这就是我应该用联合收割机做的事吗?我是不是走错路了?我如何正确地重复使用.map.assign链与两个不同的出版商?

总的来说,我对组合编程和反应性编程有点陌生,所以如果您有其他建议来改进我在这里做的每件事,请告诉我。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-29 07:59:59

处理此问题的一种方法是将您的计时器发布服务器输出映射到主题的值,并将您的通知发布服务器输出映射到主题的值。然后,您可以将主题、计时器和通知合并到一个发布服务器中,当主题的值更改、计时器触发和通知发布时,发布主题的值。

代码语言:javascript
复制
import Combine
import Foundation
import UIKit

class DataStore: ObservableObject {
    init() {
        fractionFilled = CGFloat(fillSubject.value / capacity)

        let fillSubject = self.fillSubject // avoid retain cycles in map closures
        let timer = Timer.publish(every: 3, on: .main, in: .common)
            .autoconnect()
            .map { _ in fillSubject.value }
        let foreground = NotificationCenter.default
            .publisher(for: UIApplication.willEnterForegroundNotification)
            .map { _ in fillSubject.value }
        let combo = fillSubject.merge(with: timer, foreground)
        combo
            .map { [capacity] in CGFloat($0 / capacity) }
            .sink { [weak self] in self?.fractionFilled = $0 }
            .store(in: &tickets)
    }

    let capacity: Double = 500
    @Published var fractionFilled: CGFloat

    private let fillSubject = CurrentValueSubject<Double, Never>(250)
    private var tickets: [AnyCancellable] = []
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61471559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档