Одно из требований моего приложения - возможность запускать несколько таймеров для отчетов.
Я попытался сохранить таймеры и секунды, переданные в @EnvironmentObject
с @Published
переменными, но каждый раз, когда объект обновляется, обновляется и любое представление, которое наблюдает @EnvironmentObject
.
Пример
class TimerManager: ObservableObject {
@Published var secondsPassed: [String: Int]
var timers: [String:AnyCancellable]
func startTimer(itemId: String) {
self.secondsPassed[itemId] = 0
self.timers[itemId] = Timer
.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink(receiveValue: { _ in
self.secondsPassed[itemId]! += 1
})
}
func isTimerValid(itemId: String) -> Bool {
return self.timers[itemId].isTimerValid
}
// other code...
}
Так, например, если в любом другом представлении мне нужно узнать, активен ли конкретный таймер, вызвав функцию isTimerValid
, мне нужно включить этот @EnvironmentObject
в это представление, и он не перестанет обновлять его, потому что таймер изменяет secondsPassed
, который Published
, вызывая лаги и бесполезные перерисовки.
Поэтому я кэшировал itemId
активных таймеров в другом месте, в static
struct
, который я обновляю каждый раз, когда запускаю или останавливаю таймер.
Это показалось немного взломанным, поэтому в последнее время я думал перенести все это в синглтон, например, вот так
class SingletonTimerManager {
static let singletonTimerManager = SingletonTimerManager()
var secondsPassed: [String: Int]
var timers: [String:AnyCancellable]
func startTimer(itemId: String) {
self.secondsPassed[itemId] = 0
self.timers[itemId] = Timer
.publish(every: 1, on: .main, in: .default)
.autoconnect()
.sink(receiveValue: { _ in
self.secondsPassed[itemId]! += 1
})
}
// other code...
}
и разрешить только некоторым просмотрам наблюдать за изменениями в secondsPassed
. С другой стороны, я могу переместить таймер в фоновом потоке.
Я изо всех сил пытался сделать это правильно.
Это мои Views
(правда, очень простой отрывок)
struct ContentView: View {
// set outside the ContentView
var selectedItemId: String
// timerValue: set by a publisher?
var body: some View {
VStack {
ItemView(seconds: Binding.constant(timerValue))
}
}
}
struct ItemView: View {
@Binding var seconds: Int
var body: some View {
Text("\(self.seconds)")
}
}
Мне нужно как-то наблюдать SingletonChronoManager.secondsPassed[selectedItemId]
, чтобы ItemView
обновлялись в режиме реального времени.