You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
여러 탭 간 화면 재사용과 액션 전달이 필요한 상황에서, TCA Navigation 구조를 어떻게 가져갈지 논의하고자 합니다.
TCA의 네비게이션 튜토리얼을 보면 화면 전환을 위한 Path를 enum으로 관리하는 것을 알 수 있습니다.
그리고 이 Path를 NavigationStack(path:root:destination) 혹은 NavigationStackStore(path:root:destination:)에서 사용합니다.
enum에 @Reducer매크로를 사용했을 경우 NavigationStack의 destination에서 store.case를 통해 분기하기가 편리하므로 Path를 enum으로 구현해봤습니다.
목표
자신의 탭에 속한 화면들을 사용할 수 있다
다른 탭에 구현된 화면도 재사용할 수 있다
다른 탭 화면에서 발생한 액션을 감지하고 적절히 처리할 수 있다
계획
StackState는 로 제네릭하게 선언되어 있습니다. 따라서 선언 시에 타입을 정해줘야합니다.
탭1path, 탭2path... 이 있다고 할 때, StackState<탭1path>로 선언해버리면 이 스택에는 탭2나 탭3의 path를 넣어줄 수 없게됩니다. 그래서 각 path를 포함하는 최상위의 Path가 필요하게됩니다. 이를 AppPath라고 하고 다음처럼 선언할 수 있습니다.
이제 이 AppPath를 이용해 StackState와 NavigationStack을 사용할 수 있게 됩니다.
다만 @Reducer로 확장된 코드를 확인해 보면 파라미터 없는 이니셜라이저를 사용합니다. 그래서 public init을 선언해줘야 합니다.
따라서 TCA 매크로가 생성하는 코드 구조상, 현재는 다음과 같은 형태의 이니셜라이저가 필요합니다.
@ReducerpublicenumSomePath{case home(SomeHomeFeature)case detail(SomeDetailFeature)
//컴파일을 위한 더미 이니셜라이저
publicinit(){self=.home(.init())}}
struct를 쓰지 않는 이유: Migraion1.7에서 SwitchStore를 switch로 바꾸자는 내용이 있는데, path가 struct일 때 SwitchStore를 사용하지 않고 switch에서 분기하는 방법을 찾지 못했습니다.
결론적으로 Path를 struct로 가져갈 경우 NavigationStack에서 권장되는 분기 방식과 잘 맞지 않아 enum 기반 Path를 선택했습니다.
탭별로 스택을 관리하는 StackState는 TabBarFeature.State에 다음처럼 선언하시면 됩니다.
//TabBarFeature.State
publicstructState{
//...
varsomeTab:StackState<AppPath.State>=.init()varfeedTab:StackState<AppPath.State>=.init([.feed(.home(.init()))])
//그 외에 탭바를 가리는 화면이 필요한 경우
@PresentsvarfeedTabFullScreen:AppPath.State?=nil}
추가 논의사항
이 구조를 사용할 경우 AppPath를 처리하는 리듀서가 커질 수 있다는 우려가 있습니다.
이를 extension 단위로 분리하는 방식 혹은 리듀서를 분리해주는 방식이 대안이 될 수 있을지 의견을 듣고 싶습니다.
//TabBarFeature
publicvarbody:someReducerOf<Self>{
BindingReducer()
FeedTabReducer() //피드탭에서의 액션 처리 담당해주는 리듀서
Reduce { state, action in
//...
//위의 리듀서에서 액션을 처리해주기에 이 곳에서는 feedTab 액션 분기를 할 필요가 없습니다.
case .feedTab:return.none
case .feedInternalAction:return.none
@unknown default:return.none
}}.forEach(\.feedTab, action: \.feedTab).ifLet(\.$feedFullScreen, action: \.feedFullScreen)}
View의 경우에는 destination에서 store에 따라 View로 분기하니 이런식으로 간결하게 할 수 있을 것 같아요
//TabBarFeatureView.body
TabView(selection: $store.selectedTab){
//...
NavigationStack(path: $store.scope(state: \.mapTab, action: \.mapTab)){...}
//...
NavigationStack(path: $store.scope(state: \.feedTab, action: \.feedTab)){
//path로 넘어가는 값 중 var feedTab: StackState가 비어있으면 접근하는 루트 화면
Text("먹스또")
//저의 경우에는 이 곳을 비워두고 stack을 var feedTab: StackState<AppPath.State> = .init([.feed(.home(.init()))]) 처럼 초기값을 넣어줘서 사용하고 있어서 이 화면을 사용하지는 않습니다.
} destination:{ nextStore inSelf.getView(store: nextStore).toolbarVisibility(.hidden, for:.navigationBar)}
//풀스크린의 경우도 간단하게 가능합니다.
.fullScreenCover(item: $store.scope(state: \.feedFullScreen, action: \.feedFullScreen), content:{ presentStore inSelf.getView(store: presentStore)}).tag(Tab.feed)}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
배경
여러 탭 간 화면 재사용과 액션 전달이 필요한 상황에서, TCA Navigation 구조를 어떻게 가져갈지 논의하고자 합니다.
TCA의 네비게이션 튜토리얼을 보면 화면 전환을 위한 Path를 enum으로 관리하는 것을 알 수 있습니다.
그리고 이 Path를 NavigationStack(path:root:destination) 혹은 NavigationStackStore(path:root:destination:)에서 사용합니다.
enum에 @Reducer매크로를 사용했을 경우 NavigationStack의 destination에서 store.case를 통해 분기하기가 편리하므로 Path를 enum으로 구현해봤습니다.
목표
계획
StackState는 로 제네릭하게 선언되어 있습니다. 따라서 선언 시에 타입을 정해줘야합니다.
탭1path, 탭2path... 이 있다고 할 때, StackState<탭1path>로 선언해버리면 이 스택에는 탭2나 탭3의 path를 넣어줄 수 없게됩니다. 그래서 각 path를 포함하는 최상위의 Path가 필요하게됩니다. 이를 AppPath라고 하고 다음처럼 선언할 수 있습니다.
이제 이 AppPath를 이용해 StackState와 NavigationStack을 사용할 수 있게 됩니다.
다만 @Reducer로 확장된 코드를 확인해 보면 파라미터 없는 이니셜라이저를 사용합니다. 그래서 public init을 선언해줘야 합니다.
따라서 TCA 매크로가 생성하는 코드 구조상, 현재는 다음과 같은 형태의 이니셜라이저가 필요합니다.
각 탭의 path는 아래처럼 되어있을 것인데
이니셜라이저를 추가해서 아래와 같이 만들어야합니다.
struct를 쓰지 않는 이유:
Migraion1.7에서 SwitchStore를 switch로 바꾸자는 내용이 있는데, path가 struct일 때 SwitchStore를 사용하지 않고 switch에서 분기하는 방법을 찾지 못했습니다.
결론적으로 Path를 struct로 가져갈 경우 NavigationStack에서 권장되는 분기 방식과 잘 맞지 않아 enum 기반 Path를 선택했습니다.
탭별로 스택을 관리하는 StackState는 TabBarFeature.State에 다음처럼 선언하시면 됩니다.
추가 논의사항
이 구조를 사용할 경우 AppPath를 처리하는 리듀서가 커질 수 있다는 우려가 있습니다.
이를 extension 단위로 분리하는 방식 혹은 리듀서를 분리해주는 방식이 대안이 될 수 있을지 의견을 듣고 싶습니다.
이 경우 리듀서에서 위의 메서드들을 호출하면 끝입니다.
//TabBarFeature.body Reduce { state, action in //... case let .feedTab(.element(id: _, action: action)): return Self.feed(action) case let .internalFeedAction(feedInternalAction): state.internalFeed(feedInternalAction) return .none } } .forEach(\.feedTab, action: \.feedTab) .ifLet(\.$feedFullScreen, action: \.feedFullScreen)이 경우에는 TabBarFeature.body가 훨씬 깔끔해집니다.
View의 경우에는 destination에서 store에 따라 View로 분기하니 이런식으로 간결하게 할 수 있을 것 같아요
그러면 이렇게 사용할 수 있게됩니다.
Beta Was this translation helpful? Give feedback.
All reactions