Я пытаюсь обернуть голову вокруг Combine, и при рефакторинге кода у меня возникает путаница, когда я пытаюсь пересоздать, чтобы избежать повторения.
В этом случае у меня есть значение, которое я хочу обновить в любое время:
- Спецификация c изменения предмета
- Приложение выходит на передний план
- 3-секундная ссылка sh таймер срабатывает
Так как 3-секундный таймер refre sh не публикует sh ничего до его первого запуска, я предполагаю, что мне нужно несколько издателей.
Я всегда использую только значение из темы и игнорирую любые значения, отправленные из уведомлений и таймера переднего плана.
Вот пример кода, в котором я обрабатываю значение, основанное только на одном издателе:
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
и предлагает указать -> CGFloat
в .map
, который я добавил, но затем он говорит мне, что я должен изменить CGFloat
на _
, и я получаю больше ошибок .
Это то, что я должен был делать с Комбинатом? Я иду по этому поводу все неправильно? Как я могу правильно использовать цепочку .map
и .assign
с двумя разными издателями?
Я немного новичок в области комбинированного и реактивного программирования в целом, так что если у вас есть другие предложения по улучшению того, как я Я делаю все здесь, пожалуйста, скажите мне.