はじめに
TCAでモーダル遷移時にSubViewに値を渡す方法を記録しておきます。
間違っている箇所やもっと良い方法がある箇所があるかもしれません。
もしあればコメントで教えていただけると助かります。
やりたいこと
実装
ContentView
からSubView
に遷移させます。
ContentView
ContentView.swift
import SwiftUI
struct ContentView: View {
@ObservedObject private var viewStore: ViewStoreOf<ContentReducer>
let store: StoreOf<ContentReducer>
init(store: StoreOf<ContentReducer>) {
self.store = store
self.viewStore = ViewStore(store, observe: { $0 })
}
var body: some View {
VStack(spacing: 30) {
Text(viewStore.count.description)
Button {
viewStore.send(.plusButtonTapped)
} label: {
Text("+")
}
Button {
viewStore.send(.minusButtonTapped)
} label: {
Text("-")
}
Button {
viewStore.send(.onPresentButtonTapped)
} label: {
Text("表示")
}
}
.sheet(store: store.scope(state: \.$subState, action: ContentReducer.Action.subAction)) { store in
SubView(store: store)
}
}
}
ContentReducer.swift
import Foundation
import ComposableArchitecture
public struct ContentReducer: Reducer {
public init() {}
// MARK: - State
public struct State: Equatable {
@PresentationState var subState: SubReducer.State?
var count = 0
public init() {}
}
// MARK: - Action
public enum Action: Equatable {
case subAction(PresentationAction<SubReducer.Action>)
case onPresentButtonTapped
case plusButtonTapped
case minusButtonTapped
}
// MARK: - Reducer
public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onPresentButtonTapped:
state.subState = .init(count: state.count) // ここで代入されることによってモーダルが表示される
return .none
case .subAction:
return .none
case .plusButtonTapped:
state.count += 1
return .none
case .minusButtonTapped:
state.count -= 1
return .none
}
}
.ifLet(\.$subState, action: /Action.subAction) {
SubReducer() // ここがないと`dismiss`できなくなる
}
}
}
SubView
SubView.swift
import SwiftUI
struct SubView: View {
@ObservedObject private var viewStore: ViewStoreOf<SubReducer>
let store: StoreOf<SubReducer>
init(store: StoreOf<SubReducer>) {
self.store = store
self.viewStore = ViewStore(store, observe: { $0 })
}
var body: some View {
VStack(spacing: 30) {
Text(viewStore.count.description)
Button {
viewStore.send(.onDismissButtonTapped)
} label: {
Text("閉じる")
}
}
}
}
SubReducer.swift
import Foundation
import ComposableArchitecture
public struct SubReducer: Reducer {
@Dependency(\.dismiss) var dismiss // モーダルを閉じるために必要
public init() {}
// MARK: - State
public struct State: Equatable {
var count: Int // ここで欲しい値を受け取る
public init(count: Int) {
self.count = count
}
}
// MARK: - Action
public enum Action: Equatable {
case onDismissButtonTapped
}
// MARK: - Reducer
public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onDismissButtonTapped:
return .run { _ in await self.dismiss() } // ここで閉じる
}
}
}
}
おわり
TCAはむずいです