I cannot provide exact repro steps as the bug is intermittent. But I can provide some general code as to how the StateObjects are used:
import SwiftUI
import Combine
struct RootView: View {
private let columns: [GridItem] = [
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(0..<20) { _ in
ContentView()
}
}
}
}
}
struct ContentView: View {
@StateObject
private var viewModel = ViewModel()
var body: some View {
content(viewModel.viewState)
.onAppear {
viewModel.subscribe()
}
}
@ViewBuilder
private func content(_ viewState: ViewState) -> some View {
switch viewState {
case .loading:
ProgressView()
case .content(let counter):
Text("\(counter)")
.padding()
}
}
}
extension ContentView {
enum ViewState: ObservableViewState {
case loading
case content(Int)
static var initialViewState: ViewState = .loading
}
enum Input {
case timerUpdated
}
enum Action {
case increment
case setState(ViewState)
}
class ViewModel: ObservableObject {
@Published
var viewState: ViewState = .loading
private let inputSubject = PassthroughSubject<Input, Never>()
private var subscribed = false
var internalInputPublisher: AnyPublisher<Input, Never> {
Timer.publish(every: 1, on: RunLoop.main, in: .common)
.autoconnect()
.eraseToAnyPublisher()
.map({ _ in
Input.timerUpdated
})
.eraseToAnyPublisher()
}
func subscribe() {
if subscribed {
return
}
subscribed = true
Publishers.Merge(internalInputPublisher, inputSubject)
.flatMap { input -> AnyPublisher<Action, Never> in
switch input {
case .timerUpdated:
return Just(.increment)
.eraseToAnyPublisher()
}
}
.scan(viewState, { currentState, action in
switch action {
case .setState(let state):
return state
case .increment:
if case .content(let counter) = currentState {
return .content(counter + 1)
} else {
return .content(1)
}
}
})
.removeDuplicates()
.assign(to: &$viewState)
}
func send(_ input: Input) {
inputSubject.send(input)
}
}
}
pretty much all view models follow this reactive publisher pattern for channeling view state updates to the listening view. The bug can happen to random views throughout the app
Post
Replies
Boosts
Views
Activity
will update answer*