TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Firebase Login View Changes

Forums > SwiftUI

INFO Xcode 13.1 SwiftUI Firebase 8.9.1

  • folder
  • file

FILES

  • TestApp
  • Utils
    • FirebaseManager.swift
  • Eula
    • MainEulaView.swift
    • TestView.swift
  • TestApp.swift
  • LoginView.swift
  • GoogleService-Info.plist
  • Assets
  • Preview Content

BACKGROUND I was using a template from another tutorial page. I managed to connect to a google firebase project and utilied the package dependency 'Auth' to handle user sign up, login, and password reset with email authentication.

PROBLEM MainEulaView was the default view after the user logged in with LoginView. I could not understand how to send user to TestView on a button press from within LoginView. I knew it had something to do with the login state of the user.

The RGApp.swift file contains the following code:

import SwiftUI

@main
struct RGApp: App {
    var body: some Scene {
        WindowGroup {
            MainEulaView()
        }
    }
}

On building the app, I momentarily see the MainEulaView. The LoginView then gets pushed to the active view where the user can create an account, login, or reset password. On login, the user returns to the MainEulaView.

My problem at first was I attempted to sent the user to TestView with a button as such:

Button {
                 TestView()           
                        } label: {
                            HStack {
                                Spacer()
                                Text("Accept")
                                    .frame(minWidth: 0, maxWidth: .infinity)
                                    .foregroundColor(.white)
                                    .padding(.vertical, 12)
                                    .font(.system(size: 18, weight: .bold))
                                    .overlay(RoundedRectangle(cornerRadius: 20)
                                    .stroke(Color.blue, lineWidth: 4))
                                    .background(Color.blue.cornerRadius(20))
                                Spacer()
                            }
                        }

I realized that I needed to use a navigation link and instead used the following:

Button {

                        } label: {
                            NavigationLink(destination: TestView()) {
                                Text("Button")
                                    .frame(minWidth: 0, maxWidth: .infinity)
                                    .foregroundColor(.blue)
                                    .background(Color.white)
                                    .padding(.vertical, 12)
                                    .font(.system(size: 18, weight: .bold))
                                    .cornerRadius(20)
                                    .overlay(RoundedRectangle(cornerRadius: 20)
                                    .stroke(Color.blue, lineWidth: 4))
                                Spacer()
                            }.padding()
                        }

The problem then was the button would not work. I still assumed it had something to do with the fact that I didn't know how to push TestView when it didn't know what the state of the Firebase user was.

SOLUTION Pardon my lack of understanding the correct terms. I had a feeling what the problem was. I researched basics here and advanced concepts. I read the code over and over until it started to make sense.

As a result, I determined I needed to add a Observable Object class with some code:

class TestViewModel: ObservableObject {

    @Published var errorMessage = ""
    @Published var isUserCurrentlyLoggedOut = false

    init() {

        DispatchQueue.main.async {
            self.isUserCurrentlyLoggedOut = FirebaseManager.shared.auth.currentUser?.uid == nil
        }

        fetchCurrentUser()
    }

    func fetchCurrentUser() {
        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {
            self.errorMessage = "Could not find firebase uid"
            return
        }

        FirebaseManager.shared.firestore.collection("users").document(uid).getDocument { snapshot, error in
            if let error = error {
                self.errorMessage = "Failed to fetch current user: \(error)"
                print("Failed to fetch current user:", error)
                return
            }
        }
    }

    func handleSignOut() {
        isUserCurrentlyLoggedOut.toggle()
        try? FirebaseManager.shared.auth.signOut()
    }
}

I'm not really sure what I did or why it works but it did!

If you can help me understand the logic I'd be grateful for explanations.

AM I CRAZY? This is my understanding. Please correct me.

The following code creates a class that has type annotation for ObservableObject.

class TestViewModel: ObservableObject {

The following are variables that report changes the second they're made:

@Published var errorMessage = ""
    @Published var isUserCurrentlyLoggedOut = false

The following starts the code within it:

init() {

        DispatchQueue.main.async {
            self.isUserCurrentlyLoggedOut = FirebaseManager.shared.auth.currentUser?.uid == nil
        }

        fetchCurrentUser()
    }

The following essentially assists with allowing the user to log out after determining who the user is?

func fetchCurrentUser() {
        guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {
            self.errorMessage = "Could not find firebase uid"
            return
        }

        FirebaseManager.shared.firestore.collection("users").document(uid).getDocument { snapshot, error in
            if let error = error {
                self.errorMessage = "Failed to fetch current user: \(error)"
                print("Failed to fetch current user:", error)
                return
            }
        }
    }

    func handleSignOut() {
        isUserCurrentlyLoggedOut.toggle()
        try? FirebaseManager.shared.auth.signOut()
    }
}

With all that said, I'm thinking this is the main piece that makes the whole thing work...

I am guessing its the @ObservedObject private var vm = TestViewModel()

My thoughts are that the var 'vm' is what must be fed for the view to change.

struct TestView: View {

    @ObservedObject private var vm = TestViewModel()

    var body: some View {
        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
    }
}

SUMMARY Thanks for taking time to read this. I don't expect someone to spend as much time explaining this as I did writing it out but I'd love to understand what I fixed lol. I hope this helps someone else if they are ever facing a similar situation.

3      

SOLVED

3      

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.