Home > Software engineering >  SwiftUI Navigation Link embedded in a Button with an action
SwiftUI Navigation Link embedded in a Button with an action

Time:11-19

So I've made a Button view in my body and I have set it's action to a Google Sign In action, but I want it to also transition to a new view when the sign in flow is completed. The problem is that I have set the label of the button to a Navigation Link and when I click it, it directly transitions to a next view. How can I delay the transition? For context, VoucherView is the next view I want to transition to.

Button {
                    guard let clientID = FirebaseApp.app()?.options.clientID else { return }
                    
                    // Create Google Sign In configuration object.
                    let config = GIDConfiguration(clientID: clientID, serverClientID: GlobalConstants.BACKEND_SERVER_REQUEST_ID_TOKEN)
                    
                    guard let presenter = CommonHelper.GetRootViewController() else {return}
                    
                    // Start the sign in flow!
                    GIDSignIn.sharedInstance.signIn(with: config, presenting: presenter) {user, error in
                        
                        if let error = error {
                            print (error.localizedDescription)
                            return
                        }
                        
                        guard
                            let authentication = user?.authentication,
                            let idToken = authentication.idToken
                        else {
                            print ("Something went wrong!")
                            return
                        }
                        
                        print (authentication)
                        print (idToken)
                        
                        let credential = GoogleAuthProvider.credential(withIDToken: idToken,
                                                                       accessToken: authentication.accessToken)
                        print (credential)
                        UserManager.shared.firebaseAuthentication(credential: credential)
                        signed = true
                        
                    }
                } label: {
                    NavigationLink {
                        
                        VoucherView()
                        
                    } label: {
                        Text("Sign In")
                    }

                }

Edit: After I tried using isValid as a @State variable, after every cycle of SignIn and SignOut the screen goes down.

First SignIn

FirstSignOut

SecondSignIn

SecondSignOut

CodePudding user response:

Instead of using NavigationLink inside your Button, you can use a NavigationLink with an EmptyView for a label and then activate it programmatically with the isActive parameter.

See the following example -- the Google Sign In process is replaced by just a simple async closure for brevity.

struct ContentView: View {
    @State private var navigateToNextView = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button {
                    DispatchQueue.main.asyncAfter(deadline: .now()   1) {
                        navigateToNextView = true
                    }
                } label: {
                    Text("Sign in")
                }
                NavigationLink(destination: View2(), isActive: $navigateToNextView) { EmptyView()
                }
            }
        }.navigationTitle("Test")
    }
}

struct View2 : View {
    var body: some View {
        Text("Next view")
    }
}

A couple more notes:

  • Note that there has to be a NavigationView (make sure it's just one) in the hierarchy for this to work
  • In general, async work is better done in an ObservableObject than in a View. You can consider moving your login function to an ObservableObject and making the navigateToNextView a @Published property on that object.
  • Related