LoginSignup
0
1

More than 1 year has passed since last update.

[TCA] enum State をstruct Stateに持たせる場合のpullback方法

Posted at

問題

The Composable ArchitectureSwitchStoreでの画面出し分けをする際、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を持たせたenumstructの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)
            }
        }
    }
}

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1