이 글은 TCA를 활용하면서 SUSU가 달려온 Navigation방식의 변천에 대해서 소개하는 글 입니다. SwiftUI특성상 Navigation을 정의하는 것이 결코 쉬운 일이 아니였습니다. 또한 SUSU팀이 도착한 종착역이 옳다고 할 수 없습니다. 그렇기에 비판적인 시각으로 바라봐주시면 감사합니다.
말 그대로 NavigationStack을 관장하는 Reducer가 모든 View의 Action을 관찰하면서 이를 따라가는 것 입니다. 이를 코드로 표현하면 다음과 같습니다. 모든 화면전환 로직을 최 상위 NavigationStack을 갖고있는 Reducer가 갖고 있게 됩니다. 이를 Switch 분기를 통해서 Navigation을 정의할 수 있습니다.
struct FirstNavigation {
@ObservableState
struct State: Equatable {
var isOnAppear = false
var path: StackState<Path.State> = .init()
init() {}
}
enum Action: Equatable {
case onAppear(Bool)
case goSecondScreen
case goThirdScreen
case path(StackActionOf<Path>)
}
var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
case let .onAppear(isAppear):
state.isOnAppear = isAppear
return .none
case .goThirdScreen:
state.path.append(.third(.init()))
return .none
case .goSecondScreen:
state.path.append(.second(.init()))
return .none
case let .path(.element(id: _, action: action)):
switch action {
case .second(.goThirdScreen):
state.path.append(.third(.init()))
return .none
case .third(.goSecondScreen):
state.path.append(.second(.init()))
return .none
default:
return .none
}
case .path:
return .none
}
}
.forEach(\.path, action: \.path)
}
@Reducer(state: .equatable, action: .equatable)
enum Path {
case second(SecondNavigation)
case third(ThirdNavigation)
}
}
Swift Compiler를 통한 안전한 NavigationStack Destination 생성
높은 응집성
모든 Navigation 로직을 Navigation Reducer을 통해 관리합니다. 이 방법은 TCA에서 소개하는 Navigation방식중에 하나라서, 많은 곳에서 활용합니다. 이를 관리하기가 정말 힘듭니다. 왜냐하면 뷰가 많아지면 많아질수록 코드는 읽기 힘들어지고, 빌드 속도는 매우 느려집니다. 실제 이것을 프로덕트에 적용하면 다음과 같은 코드가 탄생(?)합니다.
비대한 Reducer Body을 보면 알겠지만, 관리가 정말 힘듭니다. 사실 관리만 힘들면 그렇지만, 읽는게 힘들면 사실 그 코드는 더이상 건드리고 싶지 않기 때문에 이 방법 말고 다른 방법을 찾아야 했습니다.
누를 시 깃허브로 이동됩니다.