Search code examples
swiftxcodeswiftuiuikit

Navigating back to a UIKit View from SwiftUI View


I am working on refactoring the sign-up and I am having an issue with navigating back to a UIKit view named WelcomeViewController() from a SwiftUI view named SignUpView().

Here is the code snippet used to take the user from WelcomeViewController() to SignUpView() in WelcomeViewController.Swift:

@objc private func startSignUpFlow() {
        let hostingController = UIHostingController(rootView: SignUpView(signupState: signupState))
        navigationController?.pushViewController(hostingController, animated: true)
    }

Here is the code for SignUpView():

import SwiftUI

struct SignUpView: View {
    @State private var name = ""
    @State private var email = ""
    @State private var username = ""
    @State private var password = ""
    @State private var phoneNumber = ""
    @State private var isNavigatingToInterests = false
    @State private var coordinator: Coordinator?
    @ObservedObject var signupState: SignupState
    
    var isContinueButtonEnabled: Bool {
        !name.isEmpty &&
        !email.isEmpty && CaseSensitivity.isValidEmail(email) &&
        !username.isEmpty && CaseSensitivity.isValidUsername(username) &&
        !password.isEmpty && CaseSensitivity.isValidPassword(password) &&
        !phoneNumber.isEmpty && CaseSensitivity.isValidPhoneNumber(phoneNumber)
    }
    
    var body: some View {
        NavigationView {
            VStack {
                Image("uSTADIUM")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 191, height: 45)
                    .padding(.top, -64)
                ZStack {
                    VStack(alignment: .leading, spacing: 20) {
                        HStack {
                            Text("Personal Information")
                                .font(
                                    Font.custom("Work Sans", size: 18)
                                        .weight(.bold)
                                )
                                .foregroundColor(Color(red: 0.12, green: 0.12, blue: 0.12))
                            
                            Spacer()
                            
                            Text("Step 1")
                                .font(Font.custom("Work Sans", size: 14))
                                .fontWeight(.bold)
                                .foregroundColor(Color(red: 0.1, green: 0.68, blue: 1))
                        }
                        .padding(.horizontal, 24)
                        
                        TextFieldValidity(placeholder: "Enter your name", text: $name, isValid: true)
                        
                        TextFieldValidity(placeholder: "Enter your email", text: $email, isValid: CaseSensitivity.isValidEmail(email))
                            .autocapitalization(.none)
                        
                        TextFieldValidity(placeholder: "Enter your username", text: $username, isValid: CaseSensitivity.isValidUsername(username))
                        
                        TextFieldValidity(placeholder: "Enter your password", text: $password, isValid: CaseSensitivity.isValidPassword(password))
                        
                        TextFieldValidity(placeholder: "Enter phone number", text: $phoneNumber, isValid: CaseSensitivity.isValidPhoneNumber(phoneNumber))
                        
                        NavigationLink(destination: PickYourInterestsView(signupState: signupState), isActive: $isNavigatingToInterests) {
                            EmptyView()
                        }
                        
                        Button(action: {
                            if isContinueButtonEnabled {
                                let accountData = AccountSignup(email: email, username: username, password: password)
                                
                                AuthService.shared.signup(data: accountData) { result, error in
                                    if let error = error {
                                        print("Error: \(error.localizedDescription)")
                                    } else if let result = result {
                                        print("SignUp Successful!")
                                    }
                                }
                                
                                isNavigatingToInterests = true
                            }
                        }) {
                            Text("Continue")
                                .foregroundColor(.white)
                                .font(Font.custom("Work Sans", size: 18))
                                .padding()
                                .frame(maxWidth: .infinity)
                                .background(isContinueButtonEnabled ? Color.blue : Color.gray)
                                .cornerRadius(10)
                        }
                        .disabled(!isContinueButtonEnabled)
                    }
                    .navigationBarItems(leading: CustomBackButton(action: {
                        coordinator?.navBackToWelcome()
                    }))
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.top, 60) // Adjust the top padding as needed
                    .padding(.horizontal)
                    .padding(.bottom, 40)
                    .background {
                        Color.white
                            .cornerRadius(16)
                            .shadow(color: .black.opacity(0.08), radius: 20, x: 0, y: 6)
                            .overlay(
                                RoundedRectangle(cornerRadius: 16)
                                    .inset(by: 0.5)
                                    .stroke(Color(red: 0.88, green: 0.88, blue: 1), lineWidth: 1)
                            )
                    }
                    .padding()
                }
                .onAppear {
                    coordinator = Coordinator()
                }
            }
        }
    }
}

extension SignUpView {
    class Coordinator {
        weak var welcomeViewController: WelcomeViewController?

        init() {
            if let welcomeViewController = UIApplication.shared.keyWindow?.rootViewController as? WelcomeViewController {
                print("Found WelcomeViewController")
                self.welcomeViewController = welcomeViewController
            } else {
                print("Error: View Controller not found")
            }
        }

        func navBackToWelcome() {
            print("This function is being called.")
            welcomeViewController?.dismiss(animated: true, completion: nil)
        }
    }
}

struct CustomBackButton: View {
    var action: () -> Void
    
    var body: some View {
        Button(action: {
            action()
        }) {
            Image(systemName: "arrow.backward.square.fill")
                .imageScale(.large)
                .font(.system(size: 24))
                .foregroundColor(.gray)
                .padding(12)
        }
    }
}

The Coordinator class accepts a reference to WelcomeViewController() directly, which is then passed in onAppear. However, the error message fro the init() function in the Coordinator class is shown, and the button does not work as intended. What should be done to have the button work?


Solution

  • If you want tot dismiss back to the WelcomeViewController using the dismiss environment variable should work just fine.

    struct FirstView: View {
        @Environment(\.dismiss) var dismiss
        
        var body: some View {
            NavigationView {
                VStack {
                    Button("Dismiss me") {
                        dismiss()
                    }
                }
            }
        }
    }