我试图把我的头脑围绕着合并,当我重构代码时,当我试图重新构建以避免重复自己的时候,我会遇到一些困惑。
在这种情况下,我想随时更新一个值:
由于3秒刷新计时器在第一次启动之前不会发布任何内容,所以我假设我需要多个发布者。
我总是只使用来自主题的值,而忽略从前台通知和计时器发送的任何值。
下面是一些示例代码,其中我只基于一个发布者来处理值:
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之后隔离所有东西以使其可重用的方法:
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,所以我尝试为这个参数添加一些内容,最后得到如下结果:
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链与两个不同的出版商?
总的来说,我对组合编程和反应性编程有点陌生,所以如果您有其他建议来改进我在这里做的每件事,请告诉我。
发布于 2020-04-29 07:59:59
处理此问题的一种方法是将您的计时器发布服务器输出映射到主题的值,并将您的通知发布服务器输出映射到主题的值。然后,您可以将主题、计时器和通知合并到一个发布服务器中,当主题的值更改、计时器触发和通知发布时,发布主题的值。
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] = []
}https://stackoverflow.com/questions/61471559
复制相似问题