SwiftUI View.environmentObject (_ :) для может отсутствовать как предок этого представления .: файл - PullRequest
0 голосов
/ 25 января 2020

Я создаю свое первое приложение на IOS, используя SwiftUI и Firebase для аутентификации и хранения

Для входа в систему я использую пользовательский интерфейс Firebase по умолчанию, который настраивается с помощью подкласса FUIAuthPickerViewController, называемого MyFUIAuthPickerViewController, как подробно описано в https://firebase.google.com/docs/auth/ios/firebaseui

DefaultUI инициализируется и отображается в файле делегата сцены.

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

        self.authUI = _createAuthUI()

        guard self.authUI != nil else {
            print("No authUI")
            return
        }

        self.authUI?.delegate = self

        self.authUI?.shouldHideCancelButton = true

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            //window.rootViewController = UIHostingController(rootView: contentView)
            window.rootViewController = UINavigationController(rootViewController: MyFUIAuthPickerViewController(authUI: authUI!))
            self.window = window
            window.makeKeyAndVisible()
        }

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

import Foundation
import FirebaseUI
import Firebase

class MyFUIAuthPickerViewController: FUIAuthPickerViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }
}

Для отслеживания входа пользователя в систему я использую объект Observable, называемый Sessionstore. Код, который я адаптировал из https://benmcmahen.com/authentication-with-swiftui-and-firebase/, в котором использовался протокол Bindable старого стиля

import Foundation
import SwiftUI
import Firebase
import Combine

class SessionStore : ObservableObject
{

    @Published var user: AppUser?
    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) \(user.displayName!)")
                self.user = AppUser(uid: user.uid,displayName: user.displayName, email: user.email)
               } else {
                   // if we don't have a user, set our session to nil
                   self.user = nil
               }
           }
       }

   func signOut () -> Bool {
        do {
            try Auth.auth().signOut()
            print("signed out")
            self.user = nil
            print("user object set to nil")
            return true
        } catch {
            print("Problem encountered signing the user out")
            return false
        }
    }
}

Объект окружения присутствует в моем представлении содержимого и моем объектеdelegate

scenedelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate, FUIAuthDelegate{

    var window: UIWindow?
    var authUI: FUIAuth?
    @EnvironmentObject var appSession: SessionStore

contentview

import SwiftUI
import FirebaseUI
struct ContentView: View {

    @EnvironmentObject var session: SessionStore

        var body: some View {
            Group{
                if session.user != nil {
                    Text("Welcome \(session.user!.displayName!)")
                } else {
                    Text("Please login")
                }
            }
        }
}

struct ContentView_Previews: PreviewProvider {
    let nav = UINavigationController()
    static var previews: some View {
        ContentView().environmentObject(SessionStore())
    }
}

В расширении на моем sceneDelegate я реализую необходимые функции Firebase. При успешном входе в систему я создаю новый объект appuser, который я помещаю в sessionStore, а затем меняю rootviewcontroller на contentview, передавая в environmentObject

Extension SceneDelegate {

    func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {

        guard user != nil else {
            print("No User")
            return
        }

        print(user!.displayName!)

        let user = AppUser(uid: user!.uid,displayName: user?.email,email: user?.displayName)

        self.appSession.user = user
        let contentView = ContentView()
        self.window?.rootViewController = UIHostingController(rootView: contentView.environmentObject(SessionStore()))
        self.window?.makeKeyAndVisible()

    }


    func authPickerViewController(for authUI: FUIAuth) -> FUIAuthPickerViewController {
        return MyFUIAuthPickerViewController(authUI: authUI)
    }
}

Теперь, когда я тестирую свое приложение, после ввода имени пользователя появляется следующая ошибка и пароль

Неустранимая ошибка: ObservableObject типа SessionStore не найден. В качестве предка этого представления может отсутствовать View.environmentObject (_ :) для SessionStore. File /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros_Sim/Monoceros-39.4.3/Core/EnvironmentObject.swift , строка 55

Я подозреваю, что это связано с тем, что de environmentObject теряется в MyFUIAuthPickerViewController между потоком из моего sceneDelegate в ContentView, но как я могу предотвратить это? Мне нужно как-то расширить MyFUIAuthPickerViewController, чтобы разрешить передачу environmentObject, но как?

Надеюсь, моя проблема ясна, и вы, ребята, можете помочь.

1 Ответ

0 голосов
/ 10 марта 2020

Ваш код в SceneDelegate let contentView = ContentView() Я думаю, что это должно быть что-то вроде let contentView = ContentView().environmentObject(SessionStore())

Также кажется, что ваш SessionStore отсутствует var didChange = PassthroughSubject<SessionStore, Never>()

Первые строки вашего SessionStore должен выглядеть примерно так:

import Foundation
import SwiftUI
import Firebase
import Combine

class SessionStore : ObservableObject
{

    @Published var user: AppUser? { didSet { self.didChange.send(self) }}
    var didChange = PassthroughSubject<SessionStore, Never>()
    var handle: AuthStateDidChangeListenerHandle?

Вы хотите убедиться, что изменения распространяются на слушателей (подписчиков).

И если я прав, @EnvironmentObject var appSession: SessionStore не следует упоминать в SceneDelegate

...