問題
The Composable Architecture
のSwitchStore
での画面出し分けをする際、enumにStateを持たせる場合、以下のようにpullbackします。
AppCore.swift
enum AppState {
case firstScreen(FirstScreenState)
case secondScreen(SecondScreenState)
}
enum AppAction {
case firstScreenAction(FirstScreenAction)
case secondScreenAction(SecondScreenAction)
}
struct AppEnvironment { }
let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
firstScreenReducer
.pullback(
state: /AppState.firstScreen,
action: /AppAction.firstScreenAction,
environment: { _ in
.init()
}
),
secondScreenReducer
.pullback(
state: /AppState.secondScreen,
action: /AppAction.secondScreenAction,
environment: { _ in
.init()
}
),
Reducer { state, action, _ in
switch action {
//...
}
}
)
AppView.swift
struct AppView: View {
let store: Store<AppState, AppAction>
var body: some View {
SwitchStore(store) {
CaseLet(state: /AppState.firstScreen, action: AppAction.firstScreenAction) {
FirstScreen(store: $0)
}
CaseLet(state: /AppState.secondScreen, action: AppAction.secondScreenAction) {
SecondScreen(store: $0)
}
}
}
}
しかし以下のように、Stateを持たせたenum
をstructのState
に持たせたい場合には、当然ながら同様の方法ではpullbackすることができません。
AppCore.swift
enum AppScreen {
case firstScreen(FirstScreenState)
case secondScreen(SecondScreenState)
}
struct AppState {
+ var appScreen: AppScreen = .firstScreen(.init())
}
// 省略...
2重pullbackで解決
そこで、以下のように2重でpullbackすることで解決します。
AppCore.swift
// 省略...
let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
firstScreenReducer
.pullback(
- state: /AppState.firstScreen,
+ state: /AppScreen.firstScreen,
action: /AppAction.firstScreenAction,
environment: { _ in
.init()
}
)
+ .pullback(
+ state: \.appScreen,
+ action: /AppAction.self,
+ environment: { $0 }
+ ),
secondScreenReducer
.pullback(
- state: /AppState.secondScreen,
+ state: /AppScreen.secondScreen,
action: /AppAction.secondScreenAction,
environment: { _ in
.init()
}
)
+ .pullback(
+ state: \.appScreen,
+ action: /AppAction.self,
+ environment: { $0 }
+ ),
// 省略...
)
AppView.swift
struct AppView: View {
let store: Store<AppState, AppAction>
var body: some View {
- SwitchStore(store) {
+ SwitchStore(store.scope(state: \.appScreen)) {
- CaseLet(state: /AppState.firstScreen, action: AppAction.firstScreenAction) {
+ CaseLet(state: /AppScreen.firstScreen, action: AppAction.firstScreenAction) {
FirstScreen(store: $0)
}
- CaseLet(state: /AppState.secondScreen, action: AppAction.secondScreenAction) {
+ CaseLet(state: /AppScreen.secondScreen, action: AppAction.secondScreenAction) {
SecondScreen(store: $0)
}
}
}
}
参考