Я пытаюсь отобразить индикатор активности при выполнении асинхронного запроса. Я создал объект ActivityTracker, который будет отслеживать жизненный цикл издателя. Этот ActivityTracker является ObservableObject и будет храниться в модели представления, которая также является ObservableObject.
Кажется, что такая настройка не обновляет представление. Вот мой код:
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
VStack(spacing: 16) {
Text("Counter: \(viewModel.tracker.count)\nPerforming: \(viewModel.tracker.isPerformingActivity ? "true" : "false")")
Button(action: {
_ = request().trackActivity(self.viewModel.tracker).sink { }
}) {
Text("Request")
}
}
}
}
class ContentViewModel: ObservableObject {
@Published var tracker = Publishers.ActivityTracker()
}
private func request() -> AnyPublisher<Void, Never> {
return Just(()).delay(for: 2.0, scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
extension Publishers {
final class ActivityTracker: ObservableObject {
// MARK: Properties
@Published var count: Int = 0
var isPerformingActivity: Bool {
return count > 0
}
private var cancellables: [AnyCancellable] = []
private let counterSubject = CurrentValueSubject<Int, Never>(0)
private let lock: NSRecursiveLock = .init()
init() {
counterSubject.removeDuplicates()
.receive(on: RunLoop.main)
.print()
.sink { [weak self] counter in
self?.count = counter
}
.store(in: &cancellables)
}
// MARK: Private methods
fileprivate func trackActivity<Value, Error: Swift.Error>(
ofPublisher publisher: AnyPublisher<Value, Error>
) {
publisher
.receive(on: RunLoop.main)
.handleEvents(
receiveSubscription: { _ in self.increment() },
receiveOutput: nil,
receiveCompletion: { _ in self.decrement() },
receiveCancel: { self.decrement() },
receiveRequest: nil
)
.print()
.sink(receiveCompletion: { _ in }, receiveValue: { _ in })
.store(in: &cancellables)
}
private func increment() {
lock.lock()
defer { lock.unlock() }
counterSubject.value += 1
}
private func decrement() {
lock.lock()
defer { lock.unlock() }
counterSubject.value -= 1
}
}
}
extension AnyPublisher {
func trackActivity(_ activityTracker: Publishers.ActivityTracker) -> AnyPublisher {
activityTracker.trackActivity(ofPublisher: self)
return self
}
}
Я также пытался объявить свой ActivityTracker как @Published
, но результат тот же, мой текст не обновляется. Обратите внимание, что сохранение трекера активности непосредственно в представлении будет работать, но это не то, что я ищу.
Я что-то здесь пропустил?