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.