Bug with NavigationSplitView and views mutually exclusive #2897
-
Hi there community, I'm having a weird behavior when using .task modifier to call actions in the reducer. The issue seems simple enough, when user navigates from screen A to screen B by selecting a different option in the Sidebar and then goes back to screen A, neither .task nor .onAppear are called at all and that causes the view to be in a blank state. Here is the code: SettingsViewstruct SettingsView: View {
let store: StoreOf<SettingsFeature>
@State private var selection: NavigationSplitItem?
let items: [NavigationSplitItem] = [
NavigationSplitItem(
title: String(localized: "ScreenA"),
image: UIUXConstants.drawer,
imageFilled: UIUXConstants.drawerFilled,
section: .screenA
),
NavigationSplitItem(
title: String(localized: "ScreenB"),
image: UIUXConstants.clover,
imageFilled: UIUXConstants.cloverFilled,
section: .screenB
)
]
var body: some View {
NavigationSplitView {
// Sidebar content
List(items) { item in
HStack {
Image(
selection == item ?
item.imageFilled : item.image
)
.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
Text(item.title)
}
.padding(.horizontal, 8)
.onTapGesture {
self.selection = item
}
}
.padding(.top, 24)
.listStyle(.plain)
.toolbar {
ToolbarItem(placement: .principal) {
Text(String(localized: "Setup"))
.poppinsFont()
.foregroundStyle(.black)
.multilineTextAlignment(.center)
}
}
.navigationBarBackground()
.navigationBarTitleDisplayMode(.inline)
} detail: {
switch self.store.state {
case .screenA:
if let store = self.store.scope(state: \.screenA, action: \.screenA) {
ScreenAView(store: store)
}
case .screenB:
if let store = self.store.scope(state: \.screenB, action: \.screenB) {
ScreenBView(store: store)
}
case .none:
EmptyView()
}
}
.onChange(of: selection, { oldValue, newValue in
guard let item = newValue else { return }
self.store.send(.tabSelected(item), animation: .spring(duration: 0.5))
})
}
} Reducer@Reducer
struct SettingsFeature {
// MARK: - State
@ObservableState
enum State: Equatable {
case screenA(ScreenAFeature.State)
case screenB(ScreenBFeature.State)
case none
public init() {
self = .none
}
}
// MARK: - Action
enum Action {
case screenA(ScreenAFeature.Action)
case screenB(ScreenBFeature.Action)
case tabSelected(NavigationSplitItem)
}
// MARK: - Reducer
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .tabSelected(let item):
switch item.section {
case .hardware:
state = .screenA(.init())
case .clover:
state = .screenB(.init())
}
return .none
case .screenA:
return .none
case .screenB:
return .none
}
}
.ifCaseLet(\.hardware, action: \.hardware) {
ScreenAFeature()
}
.ifCaseLet(\.cloverIntegration, action: \.cloverIntegration) {
ScreenBFeature()
}
}
} The ScreenAView is not important only the task part: ScreenAView.task {
await self.store.send(.loadPrinters, animation: .smooth).finish()
await self.store.send(.startScanning, animation: .smooth).finish()
} ScreenA Reducermutating func refetchPrinters() {
@Dependency(\.swiftData) var context
do {
let allPrinters = try context.fetch(self.fetchDescriptor)
// ... other logic
} catch {}
}
case .loadPrinters:
state.refetchPrinters()
return .none
....
case .startScanning:
state.scanningForDevices = true
state.discoveredPrinters = []
return .run { send in
for await action in await deviceManager.discoverDevices() {
await send(handleDiscoveryAction(action), animation: .easeOut(duration: 0.15))
}
}.cancellable(id: CancelID.cancelScanning, cancelInFlight: true)
case .stopScanning:
state.scanningForDevices = false
return .cancel(id: CancelID.cancelScanning) So in summary, 5 out of 10 times it correctly calls loadPrinters and starScanning but the rest of the time is like is reusing the view?? Here can be also replicated the issue: |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi @Alexminator99, most likely this is just vanilla SwiftUI behavior. There isn't much TCA can do to cause However, if you provide a minimal project that reproduces the problem we will happily look into it. |
Beta Was this translation helpful? Give feedback.
The issue is solved in #2894. Closing this thread!!