Hello fellows,
I have been trying to use TCA in my SwiftUI project. This is my first time with SwiftUI project.
I have a WelcomeView that has two buttons Signup" and Login. I want to use NavgiationView to show those screens.
The problem is after showing SignupView
by NavigationLink
from WelcomeView
, a change I make on the SignupView
screen (for example, filling in the email information) triggers navigation pop event (shows WelcomeView
screen again).
Here is the NavigationLink
code I use in WelcomeView
;
@ViewBuilder
func prepareSignupButton(
with viewStore: ViewStore<Welcome.State, Welcome.Action>
) -> some View {
NavigationLink(
destination: IfLetStore(
self.store.scope(
state: \.signupState,
action: Welcome.Action.signupAction
),
then: SignupView.init(store:)
),
isActive: viewStore.binding(
get: \.isSignupNavigationActive,
send: Welcome.Action.setSignupNavigation(isActive:)
)
) {
PrimaryButton(title: "Signup")
}
}
@ViewBuilder
func prepareLoginButton(
with viewStore: ViewStore<Welcome.State, Welcome.Action>
) -> some View {
NavigationLink(
destination: IfLetStore(
self.store.scope(
state: \.loginState,
action: Welcome.Action.loginAction
),
then: LoginView.init(store:)
),
isActive: viewStore.binding(
get: \.isLoginNavigationActive,
send: Welcome.Action.setLoginNavigation(isActive:)
)
) {
PrimaryButton(title: "Login")
}
}
Welcome core
struct Welcome {
struct State: Equatable {
var signupState: Signup.State? = nil
var loginState: Login.State? = nil
var isSignupNavigationActive: Bool = false
var isLoginNavigationActive: Bool = false
enum Route: Equatable {
case signup
case login
}
}
enum Action: Equatable {
case onAppear
case onClickContinueAsGuest
/// Signup
case signupAction(_ action: Signup.Action)
case setSignupNavigation(isActive: Bool)
case loginAction(_ action: Login.Action)
case setLoginNavigation(isActive: Bool)
}
struct Environment {
let mainQueue: AnySchedulerOf<DispatchQueue> = .main
}
static let reducer = Reducer.combine(
Reducer<State, Action, Environment> {state, action, environment in
switch action {
case .onAppear:
return .none
case .onClickContinueAsGuest:
return .none
case .setSignupNavigation(isActive: true):
state.isSignupNavigationActive = true
state.signupState = .init()
return .none
case .setSignupNavigation(isActive: false):
state.isSignupNavigationActive = false
state.signupState = nil
return .none
case .signupAction(let signupAction):
return .none
case .loginAction:
return .none
case .setLoginNavigation(let isActive):
return .none
}
},
Signup
.reducer
.optional()
.pullback(
state: \.signupState,
action: /Action.signupAction,
environment: { env in
.init()
}
),
Login
.reducer
.optional()
.pullback(
state: \.loginState,
action: /Action.loginAction,
environment: { env in
.init()
}
)
)
}
struct Signup {
struct State: Equatable {
var user: User = .init()
var isTermsChecked: Bool = false
var isSignupButtonAvailable: Bool = false
struct User: Equatable {
var email: String = .empty
var phone: String = .empty
var countryCode: String = .empty
var name: String = .empty
var password: String = .empty
}
}
enum Action: Equatable {
case onAppear
case emailFieldChanged(_ email: String)
case phoneFieldChanged(_ phone: String)
case countryCodeChanged(_ countryCode: String)
case nameFieldChanged(_ name: String)
case passwordFieldChanged(_ password: String)
case termsCheckStateChanged(isChecked: Bool)
case signupButtonAction(isActive: Bool)
}
struct Environment {
let mainQueue: AnySchedulerOf<DispatchQueue> = .main
}
static let reducer = Reducer<State, Action, Environment> {state, action, environment in
switch action {
case .onAppear:
return .none
case .emailFieldChanged(let text):
state.user.email = text
return .none
case .phoneFieldChanged(let text):
state.user.phone = text
return .none
case .countryCodeChanged(let countryCode):
state.user.countryCode = countryCode
return .none
case .nameFieldChanged(let text):
state.user.name = text
return .none
case .passwordFieldChanged(let text):
state.user.password = text
return .none
case .termsCheckStateChanged(isChecked: true):
state.isTermsChecked = true
return .none
case .termsCheckStateChanged(isChecked: false):
state.isTermsChecked = true
return .none
case .signupButtonAction(isActive: true):
state.isSignupButtonAvailable = true
return .none
case .signupButtonAction(isActive: false):
state.isSignupButtonAvailable = false
return .none
}
}
}
When case .emailFieldChanged(let text):
triggered, NavigationView triggers pop method.