Я ищу способ получить пользователя из моей базы данных 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 является асинхронной (но, я думаю, это уже не так), и она не позволяет мне вызывать ее с ожиданием.
Я ищу способ получить пользователя из моей базы данных Firestore ПОСЛЕ завершения аутентификации.
Это правда, что вы можете получить пользовательский объект Fireabsae, когда процесс аутентификации пройдет успешно, а затем использовать его дальше в своем коде, но это не решает всю проблему, а только половину. А как насчет выхода пользователя?
Поэтому, если вы хотите отслеживать состояние аутентификации пользователя и предпринимать соответствующие действия, я рекомендую вам использовать прослушиватель состояния аутентификации . Таким образом, вы сможете отслеживать состояние аутентификации в различных компонентах или во всем приложении.
В итоге я переключился на прослушиватель снимков , чтобы избежать вызова await, а затем также изменил свой SessionUser, чтобы он имел макрос @Observable
вместо наследования ObservableObject
, поскольку были проблемы с вложенными классами, которые не обновляли представление с моими @Published
переменными. .