ios – How to exit a webframe in Swift based on a change in a firestore document


I’m building an app in Xcode and one of the screens displays a url in a web frame (newUserIntakeView). I need to navigate away from this screen to the homepageView when the fbIntakeConsultRequired variable changes from true to false.

The fbIntakeConsultRequired variable is from my firestore database and changes from true to false based on a firebase function that indicates when the user has completed the intake form on the website within the web frame. While I declare the variable in Xcode, I’m not sure how to keep the Xcode variable in sync with the firestore document field it is based on.

I’ve tried several things, including using a didSet variable alongside .onchange and .background modifiers, but it only navigates to the homepage after the app is closed and reopened, versus navigating to the homepageView the instant the fbIntakeConsultRequired variable changes.

Here’s the code that illustrates this attempt:


struct newUserIntakeView: View {

import SwiftUI
import AVKit
import GoogleSignInSwift
import GoogleSignIn
import Foundation
import Firebase
import FirebaseFirestore

    @State private var dynamicURL: String?
    @State private var goToHomepage = false
    @State private var fbIntakeConsultRequired = false {
        didSet {
            if !fbIntakeConsultRequired {
                goToHomepage = true
            }
        }
    }

extension View {
    func getRootViewController() -> UIViewController {
        guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
            return .init()
        }

        guard let root = screen.windows.first?.rootViewController else {
            return .init()
        }
        

        return root
    }
}

    var body: some View {

                VStack {
                    UcheNavBar(height: 40, alignment: .leading)
                    
                    // Use dynamicURL if available, otherwise use the default URL
                    WebView(url: URL(string: dynamicURL ?? "https://landbot.pro/v3/H-2070328-XTI33DRGBYA0JNST/index.html")!)
                }
                .navigationBarHidden(true)
                .onAppear {
                    // Call personalizeURL function when the view appears
                    personalizeURL()
                    checkFirebaseIntakeField()
                }
                // call new function to update fbI variarble, but check if there is a way to use a nvagiation link with a form that is not within the app
                .onChange(of: fbIntakeConsultRequired) { newValue in
                    if !fbIntakeConsultRequired { // Check the previous value
                        // Handle the case when intakeConsultRequired changes from true to false
                        goToHomepage = true
                        print("goToHomepage value is", goToHomepage)
                        print("fbIntakeConsultRequired value after change trigger is", fbIntakeConsultRequired)
                    }
                }
                .background(
                    NavigationLink (
                        destination: HomePageView(),
                        isActive: $goToHomepage,
                        label: EmptyView.init
                            )
                                .hidden()
                            )
                        }
                        
                        
    
    func personalizeURL() {
        
        guard let user = Auth.auth().currentUser else { return }
        
        let db = Firestore.firestore()
        let usersCollection = db.collection("users")
        
        usersCollection.document(user.uid).getDocument { document, error in
            if let document = document, document.exists {
                
                let userid = user.uid
                let nameForURL = document.data()?["firstName"] as? String ?? ""
                
                let rootIntakeURL = "https://landbot.pro/v3/H-2070328-XTI33DRGBYA0JNST/index.html"
                let newURL = rootIntakeURL + "?userid=" + userid + "&name=" + nameForURL
                
                let fbIntakeConsultRequired = document.data()?["intakeConsultRequired"] as? Bool ?? false
                print("fbIntakeConsultRequired value before change trigger is", fbIntakeConsultRequired)
                
                // Set the dynamic URL
                dynamicURL = newURL
                print("Dynamic URL is: \(newURL)")
                //print("intakeConsultRequired value before change trigger is", intakeConsultRequired)
                print("fbIntakeConsultRequired value before change trigger is", fbIntakeConsultRequired)
                
            } else {
                
                print("User does not exist within newUserIntakeView")
                
            }
            
        }
        
    }
    
    func checkFirebaseIntakeField () {
        
        guard let user = Auth.auth().currentUser else { return }
        
        let db = Firestore.firestore()
        let usersCollection = db.collection("users")
        
        usersCollection.document(user.uid).getDocument { document, error in
            if let document = document, document.exists {
                
                let fbIntakeConsultRequired = document.data()?["intakeConsultRequired"] as? Bool ?? false
                
                
            } else {
                
                print("User does not exist within checkFirebaseIntakeField")
                
            }
            
        }
    }
}

And here is the root app file that triggers the logic that makes the app navigate to the homepage after the app is closed and reopened (closing and reopening the app triggers the UCHEApp struct):


import SwiftUI
import FirebaseCore
import FirebaseAuth
import FirebaseFirestore
import GoogleSignIn

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()

        guard let clientID = FirebaseApp.app()?.options.clientID else { return false}
        return true
    }
    
    func application(_ app: UIApplication,
                    open url: URL,
                    options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        return GIDSignIn.sharedInstance.handle(url)
    }
}

@main
struct UCHEApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    @AppStorage("signIn") var isSignIn = false
    
    init() {
        UITableView.appearance().tintColor = UIColor.white
    }
    
    var body: some Scene {
        WindowGroup {
            NavigationStack {
                if !isSignIn {
                    LoginSignUpView()
                } else {
                    // Handle asynchronous Firestore operation more elegantly
                    UserContentView()
                }
            }
            .accentColor(.white)
        }
    }
}

struct UserContentView: View {
    @State private var fbIntakeConsultRequired = false
    
    var body: some View {
        Group {
            if fbIntakeConsultRequired {
                newUserIntakeView()
            } else {
                HomePageView()
            }
        }
        .onAppear {
            // Fetch user data from Firestore
            fetchUserData()
        }
    }
    
    private func fetchUserData() {
        guard let user = Auth.auth().currentUser else {
            print("No user is signed in")
            return
        }
        
        let db = Firestore.firestore()
        let usersCollection = db.collection("users")
        
        usersCollection.document(user.uid).getDocument { document, error in
            if let document = document, document.exists {
                fbIntakeConsultRequired = document.data()?["intakeConsultRequired"] as? Bool ?? false
                print("fbIntakeConsultRequired value before change trigger is", fbIntakeConsultRequired)
            } else {
                print("User does not exist within root app function")
            }
        }
    }
}

I’m aware there’s a way to achieve my desired functionality that involves constantly listening for changes in the fbIntakeConsultRequired field, but I also have heard that it isn’t scalable.

I’m also aware I could just add a button to the Navbar at the top, but that would lead to a confusing user interface because the fbIntakeConsultRequired variable changes from true to false after an intake form (in the url embedded in the web frame) is completed. If there was a button that could navigate away from the form before it was complete, users would be able to bypass it and that would lead to a poor user experience.

I’d really appreciate suggestions for how to exit a web frame view and navigate to another view in Xcode, using changes in the fbIntakeConsultRequired variable as the trigger, or some other method.

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img