Как мне повторно отрендерить свое представление в swiftUI после того, как пользователь вошел в систему с помощью Google на Firebase? - PullRequest
2 голосов
/ 29 мая 2020

Я новичок в iOS Программировании и, в частности, в swiftUI. У меня проблема в том, что я пытаюсь интегрировать аутентификацию firebase в свое приложение для управления пользователями. Теперь вход в систему и выход из системы в основном работают, проблема в том, что после входа в систему мое представление (которое условно отображает кнопку входа в Google или список содержимого не повторно отображается, поэтому я все еще вижу кнопку входа даже хотя я вошел).

Я установил наблюдаемый объект для хранения моего статуса авторизации, но, к сожалению, он не перезагружает текущего пользователя автоматически. Поэтому я настроил функцию, чтобы перезагрузить ее вручную, которую я хотел бы запускать при входе в систему. Это работает для кнопки выхода, но вход в систему завершается в AppDelegate, где по какой-то причине я не могу получить доступ к функции reloadUser ().

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

Среда:

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
    }
}

Вид, который я хотел бы визуализировать :

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
        }
    }

Также обратите внимание, что есть функция .onAppear (), которая, к сожалению, срабатывает только при первом появлении, а не при повторном появлении представления после входа пользователя в систему.

Спасибо заранее! Это было действительно неприятно.

Ответы [ 2 ]

2 голосов
/ 29 мая 2020

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

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

  1. Создание класса SessionStore, который предоставляет BindableObject , а также прослушивание аутентификации пользователей и обработку методов Auth и CRUD.
  2. Создание Модель для нашего проекта (вы это уже сделали)
  3. Добавление методов Auth в класс SessionStore.
  4. Прослушивание изменений и объединение вещей.

Начнем с SessionStore Класс: 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
}

Обратите внимание, что мы объявили, что наше свойство сеанса является необязательным User тип, который мы еще не определили. Давайте быстро сделаем один:

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
    }

}

Теперь, добавив signUp , signIn и signOut методы

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
        }
    }
}

Наконец, нам нужен способ прекратить прослушивание нашей аутентификации обработчик изменений.

class SessionStore : BindableObject {

    // prev code...

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

Наконец, Делаем наш просмотр содержимого:

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...")   
    }  
   }
  }
}
1 голос
/ 30 мая 2020

@ Гази Тозри еще раз спасибо за ваш ответ, хотя это было не то, что я хотел сделать, это подтолкнуло меня в правильном направлении. Я просто хочу изложить здесь то, что я наконец сделал, поэтому, если кто-то хочет не использовать вход по электронной почте / паролю, но вход в Google, он тоже может извлечь из этого пользу.

Я использовал Combine Framework + @Publisher Синтаксис, чтобы сделать его более читаемым, и мне также не нужны методы входа и выхода, потому что Google предоставляет их.

Кнопка SwiftUI для входа в Google будет выглядеть примерно так:

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>) {
    }
}

А используемый 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
            }
        }
    }
}

В представлении, которое проверяет для состояния аутентификации я использую функцию .onAppear () следующим образом:

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()}) 
    }
}

Для выхода из системы подойдет эта функция (из документов Firebase):

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

Надеюсь, это может помогите и кому-нибудь другому.

...