在不相关的 ViewModel 中从我的身份验证 class 订阅用户变量 - Swift/Combine
Subscribing to a user variable from my authentication class in an unrelated ViewModel - Swift/Combine
我正在尝试根据来自我的 AuthenticationState class(使用 Firebase Auth)的登录用户实例化用户配置文件。此用户配置文件是我的 UserProfileViewModel 的一部分,它应该为编辑用户配置文件的视图提供支持。
但是当 UserProfileViewModel 被实例化时,loggedInUser 似乎仍然被视为 nil,我不确定是否有一种方法可以像这样使用来自另一个 class 的 Combine 订阅来确保我订阅了身份验证状态的特定实例的 loggedInUser 发布变量。
身份验证状态由我的主应用程序文件调用,作为环境对象 - 一旦用户登录,loggedInUser 将设置为该 Firebase Auth 用户:
class AuthenticationState: NSObject, ObservableObject {
// The firebase logged in user, and the userProfile associated with the users collection
@Published var loggedInUser: User?
@Published var isAuthenticating = false
@Published var error: NSError?
static let shared = AuthenticationState()
private let auth = Auth.auth()
fileprivate var currentNonce: String?
我在我的主应用程序文件中初始化 AuthenticationState:
@main
struct GoalTogetherApp: App {
let authState: AuthenticationState
init() {
FirebaseApp.configure()
self.authState = AuthenticationState.shared
setupFirebase()
}
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authState)
}
}
}
我还有另一个 class,我想获取 loggedInUser,然后使用该用户的 uid 从 Cloud Firestore 创建或查找 userProfile:
class UserProfileViewModel: ObservableObject {
@Published var loggedInUser: User?
@Published var userProfile: UserProfile?
private let auth = Auth.auth()
private let db = Firestore.firestore()
init() {
self.loggedInUser = AuthenticationState.shared.loggedInUser
if self.loggedInUser != nil {
self.userProfile = self.loadUser()
}
}
个人资料页面应该抓住它并从 UserProfile 中提取电子邮件,但它一直显示为空白:
struct ProfilePage: View {
@ObservedObject var userProfileVM = UserProfileViewModel()
@State var email: String = ""
init() {
print("User Profile VM equals: \(String(describing: userProfileVM.userProfile))")
if userProfileVM.userProfile?.email != nil {
_email = State(initialValue: userProfileVM.userProfile!.email!)
} else {
_email = State(initialValue: "")
}
}
您可以使用 Firebase 的 Auth class 的实例方法 addStateDidChangeListener(_:)
并将通过完成处理程序传入的 User
实例分配给您自己的 属性 loggedInUser
在 AuthenticationState
中。这样,无论何时用户登录或注销,您都会收到通知 - 保持同步。像这样:
class AuthenticationState: NSObject, ObservableObject {
@Published var loggedInUser: User?
//...other properties...
static let shared = AuthenticationState()
private let auth = Auth.auth()
override init() {
super.init()
auth.addStateDidChangeListener { [weak self] (_, user) in
self?.loggedInUser = user
}
}
}
那么,您是正确的,因为您可以使用 Combine 在 AuthenticationState
实例和 UserProfileViewModel
实例之间形成数据管道。您可以使用 Combine 的 sink(receiveValue:)
方法将 UserProfileViewModel
的 loggedInUser
属性 绑定到AuthenticationState
的:
class UserProfileViewModel: ObservableObject {
@Published var loggedInUser: User?
init() {
AuthenticationState.shared.$loggedInUser
.sink { [weak self] user in
self?.loggedInUser = user
}
.store(in: &subs)
}
private var subs: Set<AnyCancellable> = .init()
}
使用 $loggedInUser
访问 @Published
提供的内置发布者。在这里您可以下沉并创建订阅。另请注意,sink(receiveValue:)
返回的 AnyCancellable
的存储。无论 UserProfileViewModel
需要多长时间,都要对此进行强烈引用。
我正在尝试根据来自我的 AuthenticationState class(使用 Firebase Auth)的登录用户实例化用户配置文件。此用户配置文件是我的 UserProfileViewModel 的一部分,它应该为编辑用户配置文件的视图提供支持。
但是当 UserProfileViewModel 被实例化时,loggedInUser 似乎仍然被视为 nil,我不确定是否有一种方法可以像这样使用来自另一个 class 的 Combine 订阅来确保我订阅了身份验证状态的特定实例的 loggedInUser 发布变量。
身份验证状态由我的主应用程序文件调用,作为环境对象 - 一旦用户登录,loggedInUser 将设置为该 Firebase Auth 用户:
class AuthenticationState: NSObject, ObservableObject {
// The firebase logged in user, and the userProfile associated with the users collection
@Published var loggedInUser: User?
@Published var isAuthenticating = false
@Published var error: NSError?
static let shared = AuthenticationState()
private let auth = Auth.auth()
fileprivate var currentNonce: String?
我在我的主应用程序文件中初始化 AuthenticationState:
@main
struct GoalTogetherApp: App {
let authState: AuthenticationState
init() {
FirebaseApp.configure()
self.authState = AuthenticationState.shared
setupFirebase()
}
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authState)
}
}
}
我还有另一个 class,我想获取 loggedInUser,然后使用该用户的 uid 从 Cloud Firestore 创建或查找 userProfile:
class UserProfileViewModel: ObservableObject {
@Published var loggedInUser: User?
@Published var userProfile: UserProfile?
private let auth = Auth.auth()
private let db = Firestore.firestore()
init() {
self.loggedInUser = AuthenticationState.shared.loggedInUser
if self.loggedInUser != nil {
self.userProfile = self.loadUser()
}
}
个人资料页面应该抓住它并从 UserProfile 中提取电子邮件,但它一直显示为空白:
struct ProfilePage: View {
@ObservedObject var userProfileVM = UserProfileViewModel()
@State var email: String = ""
init() {
print("User Profile VM equals: \(String(describing: userProfileVM.userProfile))")
if userProfileVM.userProfile?.email != nil {
_email = State(initialValue: userProfileVM.userProfile!.email!)
} else {
_email = State(initialValue: "")
}
}
您可以使用 Firebase 的 Auth class 的实例方法 addStateDidChangeListener(_:)
并将通过完成处理程序传入的 User
实例分配给您自己的 属性 loggedInUser
在 AuthenticationState
中。这样,无论何时用户登录或注销,您都会收到通知 - 保持同步。像这样:
class AuthenticationState: NSObject, ObservableObject {
@Published var loggedInUser: User?
//...other properties...
static let shared = AuthenticationState()
private let auth = Auth.auth()
override init() {
super.init()
auth.addStateDidChangeListener { [weak self] (_, user) in
self?.loggedInUser = user
}
}
}
那么,您是正确的,因为您可以使用 Combine 在 AuthenticationState
实例和 UserProfileViewModel
实例之间形成数据管道。您可以使用 Combine 的 sink(receiveValue:)
方法将 UserProfileViewModel
的 loggedInUser
属性 绑定到AuthenticationState
的:
class UserProfileViewModel: ObservableObject {
@Published var loggedInUser: User?
init() {
AuthenticationState.shared.$loggedInUser
.sink { [weak self] user in
self?.loggedInUser = user
}
.store(in: &subs)
}
private var subs: Set<AnyCancellable> = .init()
}
使用 $loggedInUser
访问 @Published
提供的内置发布者。在这里您可以下沉并创建订阅。另请注意,sink(receiveValue:)
返回的 AnyCancellable
的存储。无论 UserProfileViewModel
需要多长时间,都要对此进行强烈引用。