-
I am trying to use a
While I do understand what the message is trying to tell me, I don't know how to achieve that with TCA using BTW I am aware that when having just one path to drill down to I wouldn't even need the Here's the root reducer: import ComposableArchitecture
import SwiftUI
struct SasDocumentListDomain: Reducer {
struct State: Equatable {
var path = StackState<Path.State>()
var documents: IdentifiedArrayOf<SasDocument>
init(
documents: [SasDocument] = SasDocument.loadAll()
) {
self.documents = IdentifiedArray(uniqueElements: documents)
}
}
enum Action: Equatable {
case path(StackAction<Path.State, Path.Action>)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .path:
return .none
}
}
.forEach(\.path, action: /Action.path) {
Path()
}
}
}
extension SasDocumentListDomain {
struct Path: Reducer {
enum State: Codable, Equatable, Hashable {
case detail(SasDocumentDetailDomain.State)
}
enum Action: Equatable {
case detail(SasDocumentDetailDomain.Action)
}
var body: some Reducer<State, Action> {
Scope(state: /State.detail, action: /Action.detail) {
SasDocumentDetailDomain()
}
}
}
}
struct SasDocumentListView: View {
let store: StoreOf<SasDocumentListDomain>
var body: some View {
NavigationStackStore(
self.store.scope(
state: \.path,
action: { .path($0) }
)
) {
WithViewStore(self.store, observe: { $0 } ) { viewStore in
List {
ForEach(viewStore.documents) { document in
NavigationLink(state: SasDocumentDetailDomain
.State(document: document)) {
Text(document.title)
}
}
}
}
} destination: { state in
switch state {
case .detail:
CaseLet(
/SasDocumentListDomain.Path.State.detail,
action: SasDocumentListDomain.Path.Action.detail,
then: SasDocumentDetailView.init(store:)
)
}
}
}
}
#Preview {
SasDocumentListView(
store: Store(
initialState: SasDocumentListDomain.State(
documents: SasDocument.sampleArray
)
) {
SasDocumentListDomain()
}
)
} |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
I did encounter the same behavior. While I found a workaround, I think managing the // Child View with scoped ViewStore from parent view
MyCostomNavigationLinkView()
.onTapGesture {
viewStore.send(.myCostomSectionTapped)
}
// Parent DetailedFeature
Reduce<State, Action> { state, action in
switch action {
case .myCostomSectionTapped:
state.path.append(.myFeature(MyFeature.State()))
return .none
...
}
}
.forEach(\.path, action: /Action.path) {
Path()
}
// Parent feature view with NavigationStackStore
NavigationStackStore(self.store.scope(state: \.path, action: DetailedFeature.Action.path)) {
Text("Hello")
} destination: { state in
switch state {
case .myFeature:
CaseLet(
/DetailedFeature.Path.State.myFeature,
action: DetailedFeature.Path.Action.myFeature,
then: MyFeatureView.init(store:)
)
}
} |
Beta Was this translation helpful? Give feedback.
-
Hi @appfrosch, the problem is in your NavigationLink(state: SasDocumentDetailDomain.State(document: document)) {
Text(document.title)
} It needs to point to the path state, not to the state of the screen you want to present: NavigationLink(state: SasDocumentListDomain.Path.State.detail(document: document)) {
Text(document.title)
} This is because the navigation stack deals with the |
Beta Was this translation helpful? Give feedback.
-
Here's the code that is working in full length just for reference purposes/to help future me (when I ask myself the same question and stumble upon my own question here doing so) or another person wondering. Needless to say that @mbrandonw's tip was spot on... import ComposableArchitecture
import SwiftUI
struct SasDocumentListDomain: Reducer {
struct State: Equatable {
var path = StackState<Path.State>()
var documents: IdentifiedArrayOf<SasDocument>
init(
documents: [SasDocument] = SasDocument.loadAll()
) {
self.documents = IdentifiedArray(uniqueElements: documents)
}
}
enum Action: Equatable {
case path(StackAction<Path.State, Path.Action>)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .path:
return .none
}
}
.forEach(\.path, action: /Action.path) {
Path()
}
}
}
extension SasDocumentListDomain {
struct Path: Reducer {
enum State: Codable, Equatable, Hashable {
case detail(SasDocumentDetailDomain.State)
}
enum Action: Equatable {
case detail(SasDocumentDetailDomain.Action)
}
var body: some Reducer<State, Action> {
Scope(state: /State.detail, action: /Action.detail) {
SasDocumentDetailDomain()
}
}
}
}
struct SasDocumentListView: View {
let store: StoreOf<SasDocumentListDomain>
var body: some View {
NavigationStackStore(
self.store.scope(
state: \.path,
action: { .path($0) }
)
) {
WithViewStore(self.store, observe: { $0 } ) { viewStore in
List {
ForEach(viewStore.documents) { document in
NavigationLink(
state: SasDocumentListDomain.Path.State.detail(
SasDocumentDetailDomain.State(
document: document
)
)
) {
Text(document.title)
}
}
}
}
} destination: { state in
switch state {
case .detail:
CaseLet(
/SasDocumentListDomain.Path.State.detail,
action: SasDocumentListDomain.Path.Action.detail,
then: SasDocumentDetailView.init(store:)
)
}
}
}
}
#Preview {
SasDocumentListView(
store: Store(
initialState: SasDocumentListDomain.State(
documents: SasDocument.sampleArray
)
) {
SasDocumentListDomain()
}
)
} |
Beta Was this translation helpful? Give feedback.
Hi @appfrosch, the problem is in your
NavigationLink
:It needs to point to the path state, not to the state of the screen you want to present:
This is because the navigation stack deals with the
Path.State
enum, and so the links need to push onPath.State
.