LoginSignup
1

【SwiftUI】TCAのNavigationStackStoreを使うための最もシンプルなサンプル

Last updated at Posted at 2023-11-14

はじめに

NavigationStackStoreを使うための最もシンプルなサンプルを作成したのでコピペして使ってください

サンプルアプリ

Simulator Screen Recording - iPhone 15 - 2023-11-14 at 22.22.05.gif

実装

ParentView

ParentView.swift
import SwiftUI
import ComposableArchitecture

struct ParentView: View {
    @ObservedObject private var viewStore: ViewStoreOf<ParentReducer>

    let store: StoreOf<ParentReducer>

    public init(
        store: StoreOf<ParentReducer>
    ) {
        self.store = store
        self.viewStore = .init(store, observe: { $0 })
    }

    var body: some View {
        NavigationStackStore(store.scope(state: \.path, action: ParentReducer.Action.path)) {
            List {
                Button {
                    viewStore.send(.gotoChild1ViewButtonTapped)
                } label: {
                    Text("Child1Viewに遷移します")
                }

                Button {
                    viewStore.send(.gotoChild2ViewButtonTapped)
                } label: {
                    Text("Child2Viewに遷移します")
                }
                
                Button {
                    viewStore.send(.gotoChild3ViewButtonTapped)
                } label: {
                    Text("Child3Viewに遷移します")
                }
            }
        } destination: { state in
            switch state {
            case .child1:
                CaseLet(/ParentReducer.Path.State.child1, action: ParentReducer.Path.Action.child1) { store in
                    Child1View(store: store)
                }
            case .child2:
                CaseLet(/ParentReducer.Path.State.child2, action: ParentReducer.Path.Action.child2) { store in
                    Child2View(store: store)
                }
            case .child3:
                CaseLet(/ParentReducer.Path.State.child3, action: ParentReducer.Path.Action.child3) { store in
                    Child3View(store: store)
                }
            }
        }
    }
}

struct ParentReducer: Reducer {
    struct State: Equatable {
        var path = StackState<Path.State>()
    }

    enum Action: Equatable {
        case gotoChild1ViewButtonTapped
        case gotoChild2ViewButtonTapped
        case gotoChild3ViewButtonTapped
        case path(StackAction<Path.State, Path.Action>)
    }

    var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            switch action {
            case .gotoChild1ViewButtonTapped:
                state.path.append(.child1(.init()))
                return .none
            case .gotoChild2ViewButtonTapped:
                state.path.append(.child2(.init()))
                return .none
            case .gotoChild3ViewButtonTapped:
                state.path.append(.child3(.init()))
                return .none
            case .path:
                return .none
            }
        }
        .forEach(\.path, action: /Action.path) {
            Path()
        }
    }
    
    struct Path: Reducer {
        enum State: Equatable {
            case child1(Child1Reducer.State)
            case child2(Child2Reducer.State)
            case child3(Child3Reducer.State)
        }

        enum Action: Equatable {
            case child1(Child1Reducer.Action)
            case child2(Child2Reducer.Action)
            case child3(Child3Reducer.Action)
        }

        var body: some ReducerOf<Self> {
            Scope(state: /State.child1, action: /Action.child1) {
                Child1Reducer()
            }
            
            Scope(state: /State.child2, action: /Action.child2) {
                Child2Reducer()
            }
            
            Scope(state: /State.child3, action: /Action.child3) {
                Child3Reducer()
            }
        }
    }
}

Child1View(子要素1つ目)

Child1View.swift
import SwiftUI
import ComposableArchitecture

struct Child1View: View {
    @ObservedObject private var viewStore: ViewStoreOf<Child1Reducer>

    let store: StoreOf<Child1Reducer>

    public init(
        store: StoreOf<Child1Reducer>
    ) {
        self.store = store
        self.viewStore = .init(store, observe: { $0 })
    }

    var body: some View {
        Text("Child1View")
    }
}

struct Child1Reducer: Reducer {
    struct State: Equatable {}

    enum Action: Equatable {}

    var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            return .none
        }
    }
}

Child2View(子要素2つ目)

Child2View.swift
import SwiftUI
import ComposableArchitecture

struct Child2View: View {
    @ObservedObject private var viewStore: ViewStoreOf<Child2Reducer>

    let store: StoreOf<Child2Reducer>

    public init(
        store: StoreOf<Child2Reducer>
    ) {
        self.store = store
        self.viewStore = .init(store, observe: { $0 })
    }

    var body: some View {
        Text("Child2View")
    }
}

struct Child2Reducer: Reducer {
    struct State: Equatable {}

    enum Action: Equatable {}

    var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            return .none
        }
    }
}

Child3View(子要素3つ目)

Child3View.swift
import SwiftUI
import ComposableArchitecture

struct Child3View: View {
    @ObservedObject private var viewStore: ViewStoreOf<Child3Reducer>

    let store: StoreOf<Child3Reducer>

    public init(
        store: StoreOf<Child3Reducer>
    ) {
        self.store = store
        self.viewStore = .init(store, observe: { $0 })
    }

    var body: some View {
        Text("Child3View")
    }
}

struct Child3Reducer: Reducer {
    struct State: Equatable {}

    enum Action: Equatable {}

    var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            return .none
        }
    }
}

おわり

TCAは公式サンプルは充実してますが、それ以外の記事などがまだ少ないので増やしていきたいですね

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
1