简体   繁体   English

用户在 Firebase 上使用 Google 登录后,如何在 swiftUI 中重新呈现我的视图?

[英]How do I rerender my view in swiftUI after a user logged in with Google on Firebase?

I'm fairly new to iOS Programming and swiftUI in particular.我对 iOS 编程和 swiftUI 相当陌生。 I have the issue that I try to integrate firebase auth in my App in order to manage users.我有一个问题,我尝试在我的应用程序中集成 firebase 身份验证以管理用户。 Now the login, and log out basically works, the issue is, that after logging in, my view (which conditionally renders eighter the Google sign-in button or a list of content does not rerender so I still see the sign-in button even though I'm signed in).现在登录和注销基本上可以工作了,问题是,登录后,我的视图(有条件地呈现了谷歌登录按钮或内容列表没有重新呈现,所以我仍然看到登录按钮虽然我已登录)。

I have set an observable Object to hold my auth status but unfortunately, it does not reload the current user automatically.我设置了一个可观察的 Object 来保持我的身份验证状态,但不幸的是,它不会自动重新加载当前用户。 So I set up a function to reload it manually which I would like to trigger when logging in. This works for the logout button but the logging in finishes in the AppDelegate, where for some reason I can't access the reloadUser() function.所以我设置了一个 function 来手动重新加载它,我想在登录时触发它。这适用于注销按钮,但登录在 AppDelegate 中完成,由于某种原因我无法访问 reloadUser() function。

I'm sure there is a better way to do this and would appreciate any help!我相信有更好的方法可以做到这一点,并会感谢任何帮助!

The Environment:环境:

final class UserData: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var qrTags = qrTagData
    @Published var user: User? = Auth.auth().currentUser

    func reloadUser() -> Void {
        self.user = Auth.auth().currentUser
    }
}

The View I'd like to render:我要渲染的视图:

struct MyQuaggsList: View {
        @EnvironmentObject private var userData: UserData

        var body: some View {
            Group {
                if getLogInState() != nil {
                    VStack {
                        NavigationView {
                            List {
                                Toggle(isOn: $userData.showFavoritesOnly) {
                                    Text("Show Favorites Only")
                                }

                                ForEach(userData.qrTags) { qrTag in
                                    if !self.userData.showFavoritesOnly || qrTag.isFavorite {
                                        NavigationLink(
                                            destination: QuagDetail(qrTag: qrTag)
                                                .environmentObject(self.userData)
                                        ) {
                                            QuaggRow(qrTag: qrTag)
                                        }
                                    }
                                }
                            }
                            .navigationBarTitle(Text("My Quaggs"))
                        }
                        SignOutButton()
                    }
                } else {
                    SignInView()
                }
            }.onAppear(perform: {self.userData.reloadUser()})
        }

        func getLogInState() -> User? {
            return Auth.auth().currentUser
        }
    }

Also, note there is the.onAppear() function which unfortunately only triggers on the initial appear not on the reappearance of the view after the user logged in.另外,请注意 .onAppear() function 不幸的是,它仅在初始出现时触发,而不是在用户登录后视图重新出现时触发。

Thanks so much in advance.提前非常感谢。 It has been really frustrating.这真的很令人沮丧。

The firebase and swiftUI combination is kinda tricky at first, but you will figure out that the same pattern is used in every single project, no worries. firebase 和 swiftUI 组合起初有点棘手,但您会发现每个项目都使用相同的模式,不用担心。 Just follow my steps and customise on your project, here is our strategy.只需按照我的步骤定制您的项目,这是我们的策略。

- This might be a long answer, but i want to leave it as a refrence to all Firebase-SwiftUI user Managing in Stack OverFlow. - 这可能是一个很长的答案,但我想把它作为对 Stack OverFlow 中所有 Firebase-SwiftUI 用户管理的参考。 - -

  1. Creating a SessionStore class which provides the BindableObject , and listen to your users Authentification and Handle the Auth and CRUD methods.创建一个提供BindableObject的 SessionStore class ,并听取您的用户身份验证并处理 Auth 和 CRUD 方法。
  2. Creating a Model to our project ( you already did it)为我们的项目创建一个 Model(你已经这样做了)
  3. Adding Auth methods in SessionStore Class.在 SessionStore Class 中添加 Auth 方法。
  4. Listening for changes and putting things together.倾听变化并将事情放在一起。

Let s start by SessionStore Class: import SwiftUI import Firebase import Combine让我们从SessionStore Class 开始: import SwiftUI import Firebase import Combine

class SessionStore : BindableObject {
    var didChange = PassthroughSubject<SessionStore, Never>()
    var session: User? { didSet { self.didChange.send(self) }}
    var handle: AuthStateDidChangeListenerHandle?

    func listen () {
        // monitor authentication changes using firebase
        handle = Auth.auth().addStateDidChangeListener { (auth, user) in
            if let user = user {
                // if we have a user, create a new user model
                print("Got user: \(user)")
                self.session = User(
                    uid: user.uid,
                    displayName: user.displayName
                )
            } else {
                // if we don't have a user, set our session to nil
                self.session = nil
            }
        }
    }

    // additional methods (sign up, sign in) will go here
}

Notice that we've declared that our session property is an optional User type, which we haven't yet defined.请注意,我们已经声明我们的 session 属性是一个可选的用户类型,我们还没有定义它。 Let's quickly make one:让我们快速制作一个:

class User {
    var uid: String
    var email: String?
    var displayName: String?

    init(uid: String, displayName: String?, email: String?) {
        self.uid = uid
        self.email = email
        self.displayName = displayName
    }

}

Now, adding signUp , signIn and signOut methods现在,添加signUpsignInsignOut方法

class SessionStore : BindableObject {

    // prev code...

    func signUp(
        email: String,
        password: String,
        handler: @escaping AuthDataResultCallback
        ) {
        Auth.auth().createUser(withEmail: email, password: password, completion: handler)
    }

    func signIn(
        email: String,
        password: String,
        handler: @escaping AuthDataResultCallback
        ) {
        Auth.auth().signIn(withEmail: email, password: password, completion: handler)
    }

    func signOut () -> Bool {
        do {
            try Auth.auth().signOut()
            self.session = nil
            return true
        } catch {
            return false
        }
    }
}

Finally, we need a way to stop listening to our authentication change handler.最后,我们需要一种方法来停止监听我们的身份验证更改处理程序。

class SessionStore : BindableObject {

    // prev code...

    func unbind () {
        if let handle = handle {
            Auth.auth().removeStateDidChangeListener(handle)
        }
    }
}

Finally, Making our content view:最后,制作我们的内容视图:

import SwiftUI

struct ContentView : View {

  @EnvironmentObject var session: SessionStore
  var body: some View {
    Group { 
     if (session.session != nil) {  
      Text("Hello user!") 
     } else {
        Text("Our authentication screen goes here...")   
    }  
   }
  }
}

@Ghazi Tozri thanks again for your answer, while it wasn't what I wanted to do exactly it pushed me in the right direction. @Ghazi Tozri 再次感谢您的回答,虽然这不是我想要做的,但它把我推向了正确的方向。 I just want to put here what I finally did so if anyone wants to not use Email / Password sign-in but Google sign-in they can benefit from it too.如果有人不想使用 Email / 密码登录但谷歌登录,他们也可以从中受益,我只想把我最终这样做的内容放在这里。

I used the Combine Framework + the @Publisher Syntax to make it a bit more readable and I also don't need the Signing in and out Methods because Google Provides them.我使用了组合框架 + @Publisher 语法使其更具可读性,并且我也不需要登录和注销方法,因为 Google 提供了它们。

The SwiftUI Button for Google sign-in would look something like this:用于 Google 登录的 SwiftUI 按钮如下所示:

struct GoogleSignIn : UIViewRepresentable {
    @EnvironmentObject private var userData: SessionStore

    func makeUIView(context: UIViewRepresentableContext<GoogleSignIn>) -> GIDSignInButton {
        let button = GIDSignInButton()
        button.colorScheme = .dark
        GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.last?.rootViewController

        //If you want to restore a session
        //GIDSignIn.sharedInstance()?.restorePreviousSignIn()
        return button
    }

    func updateUIView(_ uiView: GIDSignInButton, context: UIViewRepresentableContext<GoogleSignIn>) {
    }
}

And the Used SessionStore like this:像这样使用的 SessionStore:

import SwiftUI
import Combine
import Firebase
import GoogleSignIn

final class SessionStore: ObservableObject {
    @Published var showFavoritesOnly = false
    @Published var qrTags = qrTagData
    @Published var session: User?
    var handle: AuthStateDidChangeListenerHandle?

    func listen() {
        handle =  Auth.auth().addStateDidChangeListener { (auth, user) in
            if let user = user {
                self.session = user
            } else {
                self.session = nil
            }
        }
    }
}

In a view that checks for the authentication state I use the.onAppear() function like this:在检查身份验证 state 的视图中,我使用了.onAppear() function,如下所示:

struct UserProfile: View {
    @EnvironmentObject private var session: SessionStore

    var body: some View {
        VStack {
            if session.session != nil {
                SignOutButton()
            } else {
                SignInView()
            }
        }.onAppear(perform: {self.session.listen()}) 
    }
}

To sign out this function will do (from the Firebase Docs):要退出此 function 将执行(来自 Firebase 文档):

func signOut() -> Void {
        let firebaseAuth = Auth.auth()
        do {
            try firebaseAuth.signOut()
        }
        catch let signOutError as NSError {
            print ("Error signing out: %@", signOutError)
        }
    }

Hope this can help somebody else too.希望这也可以帮助其他人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM