I have a login screen on which I programmatically push to the next screen using a hidden NavigationLink tied to a state variable. The push works, but it seems to push twice and pop once, as you can see on this screen recording:
This is my view hierarchy:
App
NavigationView
LaunchView
LoginView
HomeView
App:
var body: some Scene {
WindowGroup {
NavigationView {
LaunchView()
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.environmentObject(cache)
}
}
LaunchView:
struct LaunchView: View {
@EnvironmentObject var cache: API.Cache
@State private var shouldPush = API.shared.accessToken == nil
func getUser() {
[API call to get user, if already logged in](completion: { user in
if let user = user {
// in our example, this is NOT called
// thus `cache.user.hasData` remains `false`
cache.user = user
}
shouldPush = true
}
}
private var destinationView: AnyView {
cache.user.hasData
? AnyView(HomeView())
: AnyView(LoginView())
}
var body: some View {
if API.shared.accessToken != nil {
getUser()
}
return VStack {
ActivityIndicator(style: .medium)
NavigationLink(destination: destinationView, isActive: self.$shouldPush) {
EmptyView()
}.hidden()
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
This is a cleaned version of my LoginView:
struct LoginView: View {
@EnvironmentObject var cache: API.Cache
@State private var shouldPushToHome = false
func login() {
[API call to get user](completion: { user in
self.cache.user = user
self.shouldPushToHome = true
})
}
var body: some View {
VStack {
ScrollView(showsIndicators: false) {
// labels
// textfields
// ...
PrimaryButton(title: "Anmelden", action: login)
NavigationLink(destination: HomeView(), isActive: self.$shouldPushToHome) {
EmptyView()
}.hidden()
}
// label
// signup button
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
The LoginView itself is child of a NavigationView.
The HomeView is really simple:
struct HomeView: View {
@EnvironmentObject var cache: API.Cache
var body: some View {
let user = cache.user
return Text("Hello, \(user.contactFirstname ?? "") \(user.contactLastname ?? "")!")
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
What's going wrong here?
Update:
I've realized that the issue does not occur, when I replace LaunchView() in App with LoginView() directly. Not sure how this is related...
Update 2:
As Tushar pointed out below, replacing destination: destinationView with destination: LoginView() fixes the problem – but obviously lacks required functionality.
So I played around with that and now understand what's going on:
LaunchViewis renderedLaunchViewfinds there's no user data yet, so pushes toLoginView- upon user interaction,
LoginViewpushes toHomeView - at this point, the
NavigationLinkinsideLaunchViewis called again (idk why but a breakpoint showed this), and since there is user data now, it renders theHomeViewinstead of theLoginView.
That's why we see only one push animation, and the LoginView becoming the HomeView w/o any push animation, b/c it's replaced, essentially.
So now the objective is preventing LaunchView's NavigationLink to re-render its destination view.