Open defagos opened 1 day ago
This is not an issue specific to Pillarbox but rather a SwiftUI behavior. Consider the following code sample:
import SwiftUI
final class ViewModel: ObservableObject {
@Published private(set) var count = 0
func update() {
count = 1
count = 2
}
func reset() {
count = 0
}
}
struct ContentView: View {
@StateObject private var model = ViewModel()
var body: some View {
VStack(spacing: 20) {
Text("Count: \(model.count)")
Button(action: model.update) {
Text("Update")
}
Button(action: model.reset) {
Text("Reset")
}
}
.onChange(of: model.count) { old, new in
print("--> onChange: \(new)")
}
.onReceive(model.$count) { new in
print("--> onReceive: \(new)")
}
}
}
#Preview {
ContentView()
}
The output when tapping on the Update button is:
--> onReceive: 1
--> onReceive: 2
--> onChange: 2
which clearly shows that changes are coalesced by onChange(of:initial:_:)
, since onChange
actually checks for a value change only when a view body is updated, not through actual observation.
Fortunately it suffices to listen to the associated publisher stream to receive all values.
Note that onReceive
is bit tricky as well. Consider the following code:
import Combine
import SwiftUI
struct ContentView: View {
@State private var count = 0
var body: some View {
Stepper(value: $count) {
Text("Count: \(count)")
}
.padding()
.onReceive(publisher()) { new in
print("--> onReceive: \(new)")
}
}
private func publisher() -> AnyPublisher<Int, Never> {
Just(Int.random(in: 0...4)).eraseToAnyPublisher()
}
}
#Preview {
ContentView()
}
When tapping on the stepper we get logs similar to:
--> onReceive: 0
--> onReceive: 1
--> onReceive: 2
--> onReceive: 4
--> onReceive: 3
If we add a print()
operator to the publisher itself we can namely clearly observe that each body refresh makes onReceive
subscribe to the publisher again.
We must therefore be careful if we want to provide an onReceive
-based helper that only reports actual value updates.
As an developer using Pillarbox I would like to be able to reliably observe playback state updates during playback. When I observe such changes with
onChange
APIs, though, I very often miss some states.An example: When playing content repeatedly with an
repeatMode
setting and observing the change withonChange
, I would like to trigger some action when reaching the.ended
state. But very often I only receive a change to a subsequent state, missing this state entirely.Acceptance criteria
Tasks