Обработка ошибки аутентификации Firebase в приложении SwiftUI Combine

У меня есть приложение, в котором пользователи могут зарегистрироваться и войти в систему с помощью Firebase. Однако я не могу предупредить пользователя о каких-либо ошибках в представлении.

Сначала у нас есть UserStore, который является ObservableObject и инициализируется как EnvironmentObject при настройке представления в SceneDelegate.

let appView = AppView().environmentObject(userStore)

  if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: appView)
        self.window = window
        window.makeKeyAndVisible()

}

Затем мы регистрируемся или авторизуемся в View вот так.

В поле зрения

self.userStore.logIn(email: self.email, password: self.password)
self.isLoggingIn = true

if self.userStore.failedToLogin {
   self.isLoggingIn = false
   self.alertTitle = "There seems to be a problem"
   self.alertBody = self.userStore.errorMessage
   self.showingAlert = true
}

Затем фактический метод должен обновить значения свойств UserStore, которые затем обновят представление и отобразят предупреждение, однако это не так.

Войти

// Session Properties
@Published var isLoggedIn = false {didSet{didChange.send() }}
@Published var isNewUser = false {didSet{didChange.send() }}
@Published var failedToCreateAccount = false {didSet{didChange.send() }}
@Published var failedToLogin = false {didSet{didChange.send() }}
@Published var errorMessage = "" {didSet{didChange.send() }}

init () {
  handle = Auth.auth().addStateDidChangeListener { (auth, user) in
    if let user = user {
      self.session = user
      self.isLoggedIn = true
      //Change isNewUser is user document exists?
   } else {
      self.session = nil
      self.isLoggedIn = false
    }
  }
}

func logIn(email: String, password: String) {

    Auth.auth().signIn(withEmail: email, password: password) { [weak self] user, error in

        print("Signed In")

        if(error != nil) {
            print("Failed to login")

            self!.failedToLogin = true
            self!.errorMessage = ("\(String(describing: error))")
            print(error!)
            return
        } else if error == nil {
            print("Success Logging In")

        }
    }
}

AppView определяет, какое представление загружается, в зависимости от того, вошел ли пользователь в систему.

Просмотр приложений

        if !userStore.isLoggedIn {
            LoginView().transition(.opacity)
        }

        if userStore.isLoggedIn  {
            ContentView().transition(.opacity)
        }

Сообщения об ошибках банкомата не отображаются; представление входа в систему также отображается незадолго до основного представления.

Как правильно отображать сообщения об ошибках в представлении?


person RileyDev    schedule 12.02.2020    source источник
comment
Кажется, проблема в том, что userStore.login - это асинхронный процесс. Т.е. вызов if self.userStore.failedToLogin выполняется до возврата userStore.login. Таким образом, userStore.failedToLogin по-прежнему false, и код в условном операторе никогда не будет выполнен. Есть два способа обойти это: 1. Реализовать завершающее замыкание на userStore.logIn и переместить код, отображающий ошибку, в закрытие. 2. Сделать userStore.failedToLogin издателем и подписать на него видимость вашего предупреждения.   -  person Peter Friese    schedule 13.02.2020
comment
@PeterFriese 100% по пункту комментарий. Я отправлю это как ответ. OP - эта строка self.isLoggingIn = true и следующий за ней код будут выполняться до входа пользователя в систему - код быстрее, чем в Интернете, и вам нужно выполнить следующий «шаг» из закрытия входа Firebase. См. Мой ответ на этот вопрос для дополнительного чтения.   -  person Jay    schedule 13.02.2020
comment
pod 'CombineFirebase / Auth' значительно упростит вашу проблему. Вместо обратного вызова вы можете просто использовать шаблон объединения издателей и использовать мощные операторы объединения.   -  person kumar shivang    schedule 11.03.2020


Ответы (2)


API-интерфейсы Firebase являются асинхронными просто потому, что они обращаются к удаленной системе через Интернет, что занимает немного времени. То же самое, кстати, касается доступа к локальному диску. В этом сообщении в блоге объясняется об этом подробнее.

Следовательно, userStore.login - асинхронный процесс. Т.е. вызов if self.userStore.failedToLogin выполняется до возврата userStore.login. Таким образом, userStore.failedToLogin по-прежнему false, и код в условном операторе никогда не будет выполнен.

Есть два способа обойти это:

  1. Реализуйте завершающее закрытие на userStore.logIn и переместите код, отображающий ошибку, в закрытие.
  2. Сделайте userStore.failedToLogin издателем и подпишитесь на видимость вашего оповещения
person Peter Friese    schedule 13.02.2020
comment
Большое вам спасибо! Да, теперь это довольно очевидно. Также я прочитал ваш пост на Medium о Firebase и SwiftUI, Combine; есть идеи, когда выйдет часть 3 с Apple Sign in? Поскольку это то, над чем я работаю сейчас .. - person RileyDev; 14.02.2020
comment
Спасибо за добрые слова, @RileyDev! Пока мы говорим, работаем над третьей частью, надеемся выпустить ее на следующей неделе! - person Peter Friese; 14.02.2020
comment
Часть 3 из серии, о которой спрашивал @RileyDev, теперь доступна по адресу peterfriese.dev/replicating- напоминание-swiftui-firebase-part3 - person Peter Friese; 27.03.2020

Используйте Combine Firebase: https://github.com/rever-ai/CombineFirebase

Это оболочка comb-swiftui вокруг api firebase, поэтому вы можете использовать шаблон издателя swift-ui для firebase.

person kumar shivang    schedule 14.06.2020