Как загрузить пользовательские данные из Firestore и обновить свое представление после асинхронной аутентификации в Swift?

Я ищу способ получить пользователя из моей базы данных Firestore ПОСЛЕ завершения аутентификации и получения UID для пользователя, но ДО того, как мое представление загрузится на SwiftUI/Swift 5+. Или, альтернативно, я могу использовать ProgressView между ними.

В моем представлении ContentView у меня есть условное предложение о том, зарегистрирован ли пользователь или нет. Это поле в моем документе пользователя в Firebase, к которому мне нужен доступ.

Проблема, с которой я сталкиваюсь, заключается в том, что когда мое представление загружается, оно загружает значения по умолчанию моей структуры User, когда мне нужны значения из базы данных.

Вот мое решение прямо сейчас:

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

class SessionStore : ObservableObject {
  u/Published var session: SessionUser? { didset { self.didChange.send(self) } }
  var handle: AuthStateDidChangeListenerHandle?

  // Auth listening by running addStateDidChangeListener
  func listen() { ... }

  // Unbind the Auth listener
  func unbind() { ... }

  func handleGoogleLogin() {
    GIDSignIn.sharedInstance.signIn(withPresenting: getRootViewController()) {
              signInResult, err in
    ...
    
      signInResult.user.refreshTokensIfNeeded { user, error in
      ...

        Auth.auth().signIn(with: credential) { result, err in
          print("success!")
          // TODO: run fetch user here somehow
        }
      }
    }
  }
}

Затем в моем объекте SessionUser у меня есть вызов для получения данных из коллекции в Firestore:

class SessionUser : ObservableObject {
  @Published var user: User?
  @Published var isUserOnboarded: Bool
  var uid: String
  
  init(uid) { ... }

  func fetchSessionUser() async {
    do {
      // Tries to get user from the datastore first in case user exists already
      if doesUserExist() {
        // Get user from Firestore db
        let doc = try await db.collection("users").document("\(userId)")
        if doc.exists {
          self.user = try document.data(as: User.self)
          self.isUserOnboarded = self.user.isUserOnboarded
        }
      } else {
        // Create user
      }
    }
  }
}

Для потомков, вот представление ContentView:

struct ContentView: View {
  @StateObject var session: SessionStore = SessionStore()

  var body: some View {
    VStack {
      if session.session != nil || Auth.auth().currentUser != nil {
        if session.session?.isUserOnboarded {
          MainView()
        } else {
          OnboardingView()
        }
      } else {
        StartPageView()
      }
   }
}

Я пробовал много вещей, таких как экранирование объекта в вызове выборки, создание инкапсулированного класса с опубликованной переменной для isUserOnboarded (как показано выше) и экспериментирование с вызовами DispatchQueue.main.async, но ничего не помогло. до сих пор.

Предыдущие ответы на StackOverflow/online показывают, что процедура аутентификации для Firebase является асинхронной (но, я думаю, это уже не так), и она не позволяет мне вызывать ее с ожиданием.


63
2

Ответы:

Я ищу способ получить пользователя из моей базы данных Firestore ПОСЛЕ завершения аутентификации.

Это правда, что вы можете получить пользовательский объект Fireabsae, когда процесс аутентификации пройдет успешно, а затем использовать его дальше в своем коде, но это не решает всю проблему, а только половину. А как насчет выхода пользователя?

Поэтому, если вы хотите отслеживать состояние аутентификации пользователя и предпринимать соответствующие действия, я рекомендую вам использовать прослушиватель состояния аутентификации . Таким образом, вы сможете отслеживать состояние аутентификации в различных компонентах или во всем приложении.


Решено

В итоге я переключился на прослушиватель снимков , чтобы избежать вызова await, а затем также изменил свой SessionUser, чтобы он имел макрос @Observable вместо наследования ObservableObject, поскольку были проблемы с вложенными классами, которые не обновляли представление с моими @Published переменными. .