Проверка аутентификации пользователя с помощью Google Sign In и SwiftUI

Я успешно настроил аутентификацию в своем приложении, используя Google Sign-In, где я могу вернуть пользователя Firebase. Я пытаюсь настроить экран входа, который отображается только при отсутствии аутентифицированного пользователя Firebase, однако с моим текущим кодом экран входа всегда виден, даже если я постоянно возвращаю аутентифицированного пользователя.

Я реализовал функцию didSignInFor в AppDelegate

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
      // ...
      if let error = error {
        print(error.localizedDescription)
        return
      }

      guard let authentication = user.authentication else { return }
      let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                        accessToken: authentication.accessToken)
      // ...
        Auth.auth().signIn(with: credential) { (authResult, error) in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            let session = FirebaseSession.shared

            if let user = Auth.auth().currentUser {
                session.user = User(uid: user.uid, displayName: user.displayName, email: user.email)
                print("User sign in successful: \(user.email!)")
            }
        }
    }

а также несколько строк в didFinishLaunchingWithOptions, которые устанавливают свойство isLoggedIn моего ObservableObject FirebaseSession

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        FirebaseApp.configure()

        let auth = Auth.auth()

        if auth.currentUser != nil {
            FirebaseSession.shared.isLoggedIn = true
            print(auth.currentUser?.email!)
        } else {
            FirebaseSession.shared.isLoggedIn = false
        }

        //Cache
        let settings = FirestoreSettings()
        settings.isPersistenceEnabled = false

        GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
        GIDSignIn.sharedInstance().delegate = self

        return true
    }

My ObservableObject

class FirebaseSession: ObservableObject {

        static let shared = FirebaseSession()
        init () {}

        //MARK: Properties
        @Published var user: User?
        @Published var isLoggedIn: Bool?

        @Published var items: [Thought] = []

        var ref: DatabaseReference = Database.database().reference(withPath: "\(String(describing: Auth.auth().currentUser?.uid ?? "Error"))")

        //MARK: Functions
        func listen() {
            _ = Auth.auth().addStateDidChangeListener { (auth, user) in

                if auth.currentUser != nil {
                    self.isLoggedIn = true
                }

                if let user = user {
                    self.user = User(uid: user.uid, displayName: user.displayName, email: user.email)
                } else {
                    self.user = nil
                }
            }
        }
    }

Наконец, я выполняю проверку аутентификации в главном представлении моего приложения здесь, получая доступ к FirebaseSession через мой ObservedObject

struct AppView: View {

    @ObservedObject var session = FirebaseSession.shared

    @State var modalSelection = 1
    @State var isPresentingAddThoughtModal = false

    var body: some View {
        NavigationView {
            Group {
                if session.isLoggedIn == true {
                    ThoughtsView()
                } else {
                    SignInView()
                }
            }
        }
    }
}

Как упоминалось выше, мой чек не работает. Несмотря на то, что мой пользователь аутентифицирован, SignInView всегда виден.

Как я могу успешно проверять аутентификацию пользователя каждый раз, когда мое приложение загружается?

ОБНОВЛЕНИЕ

Теперь я могу проверить аутентификацию при загрузке приложения, но после внедрения решения Сохила я не наблюдаю изменений в реальном времени в моем ObservableObject FirebaseSession. Я хочу наблюдать за изменениями в FirebaseSession, чтобы после входа нового пользователя тело AppView было перерисовано и отображало ThoughtsView вместо SignInView. В настоящее время мне нужно перезагрузить приложение, чтобы проверка произошла после аутентификации.

Как я могу наблюдать изменения в FirestoreSession по сравнению с AppView?


person Austin Berenyi    schedule 27.11.2019    source источник


Ответы (3)


Вам нужно сделать что-то вроде этого. Я не пробовал запускать это, поэтому я не уверен, есть ли опечатки...

class SessionStore : ObservableObject {
  @Published var session: FIRUser?
  var isLoggedIn: Bool { session != nil}
  var handle: AuthStateDidChangeListenerHandle?

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

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

в вашем компоненте:


struct AppView: View {
    @ObservedObject var session = SessionStore()

    var body: some View {
        Group {
          if session.isLoggedIn {
            ...
          } else {
            ...
          }
      }
    }
}

Обратите внимание, что здесь важно то, что объект, который изменяется, — это @Published. Вот как вы будете получать обновления в вашем представлении.

person Gil Birman    schedule 27.11.2019

Ваша проблема заключается в доступе к объектам и их стоимости. Значит, в файле AppDelegate.swift вы создаете объект FirebaseSession и присваиваете значения, но затем в файле AppView вы снова создаете новый объект FirebaseSession, который создает новый экземпляр класса, и все значения заменяются на значения по умолчанию.

Итак, вам нужно использовать один и тот же объект на протяжении всего жизненного цикла нашего приложения, что можно сделать, определив let session = FirebaseSession() глобально или создав Класс Singleton, как показано ниже.

class FirebaseSession: ObservableObject {

    static let shared = FirebaseSession()

    private init () {}

    //Properties...
    //Functions...
}

Затем вы можете получить доступ к объекту shared следующим образом:

FirebaseSession.shared.properties

Таким образом, ваши назначенные значения будут сохранены в течение жизненного цикла приложения.

person Sohil R. Memon    schedule 27.11.2019
comment
Это решение работает в том смысле, что теперь я могу успешно проверить свою аутентификацию из AppView, однако я не могу наблюдать изменения в моем ObservableObject FirebaseSession из AppView, как мне нужно. Моя цель состоит в том, чтобы после входа нового пользователя FirebaseSession.isLoggedIn переключалось, вызывая перерисовку AppView. С вашим вышеуказанным решением я должен перезагрузить приложение, чтобы проверка произошла, потому что FirebaseSession не наблюдается. - person Austin Berenyi; 27.11.2019

Я не знаю, будет ли это полезно для вас, но я прочитал ваш вопрос, потому что я нашел решение для аналогичной проблемы для себя, поэтому я нашел его и собираюсь поделиться с вами.

Я подумал об использовании делегата. Поэтому я создал протокол с именем ApplicationLoginDelegate в своем классе AppDelegate.

Я определяю протокол следующим образом:

protocol ApplicationLoginDelegate: AnyObject {
    func loginDone(userDisplayName: String)
}

И в классе AppDelegate я определяю loginDelegate:

weak var loginDelegate: ApplicationLoginDelegate?

Вы можете вызвать функцию делегата в вашей функции didSignIn.

Auth.auth().signIn(with: credential) { (res, error) in

      if let err = error {
          print(err.localizedDescription)
          return
      } else {
          self.loginDelegate?.loginDone(userDisplayName: (res?.user.displayName)!)
      }
  }

Итак, в SceneDelegate вы используете своего делегата, как я вам показываю:

class SceneDelegate: UIResponder, UIWindowSceneDelegate, ApplicationLoginDelegate {

var window: UIWindow?
var eventsVM: EventsVM?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    // Set the app login delegate
    (UIApplication.shared.delegate as! AppDelegate).loginDelegate = self

    // Environment Objects
    let eventsVM = EventsVM()

    self.eventsVM = eventsVM

    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView()
        .environmentObject(eventsVM)

    // Use a UIHostingController as window root view controller.
    [...]
}

func loginDone(userDisplayName: String) {
    guard let eventsVM = self.eventsVM else { return }
    eventsVM.mainUser = User(displayName: userDisplayName)
}

Поэтому, когда вы обновили свой объект среды, который сам имеет объект @Publisced (например, объект пользователя), вы получаете свои обновления везде, где вы определяете и вызываете объект среды!

Вот и все!

person Pask    schedule 22.05.2020