swift - 在不相关的 ViewModel 中从我的身份验证类订阅用户变量 - Swift/Combine
问题描述
我正在尝试根据我的 AuthenticationState 类中的登录用户实例化用户配置文件(使用 Firebase Auth)。此用户配置文件是我的 UserProfileViewModel 的一部分,它应该为编辑用户配置文件的视图提供动力。
但是,当 UserProfileViewModel 被实例化时,loggedInUser 似乎仍然被视为 nil,我不确定是否有办法可以使用来自另一个类的 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)
}
}
}
我有另一个类,我想获取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: "")
}
}
解决方案
您可以使用addStateDidChangeListener(_:)
Firebase 的 Auth 类的实例方法,并将User
通过完成处理程序传入的实例分配给您自己的loggedInUser
in属性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
}
}
}
AuthenticationState
然后,您是正确的,因为您可以使用 Combine 在您的实例和实例之间形成数据管道UserProfileViewModel
。您可以使用Combineinit()
的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()
}
Using$loggedInUser
访问由@Published
. 在这里您可以下沉并创建订阅。另请注意,AnyCancellable
由sink(receiveValue:)
. 无论需要多长时间,都要对此保持强烈的参考UserProfileViewModel
。
推荐阅读
- jquery - 检查奇数或偶数子并使用 jQuery 将类添加到父级
- php - 循环多维数组以更新 PHP 中的值
- scala - Kafka 流:在摄取时间加入
- sql - 子查询返回多个值,不允许出现错误
- androidx - 我应该如何将我的 AppTheme 样式转换为 Material 组件
- go - 当在银杏执行 defer func 时
- vb6 - 与 Outlook 2019 一起崩溃,而不是更早?
- python - 如何将 api JSON 输出转换为数据框?
- python - 每次调用其父函数时是否重新定义内部函数?
- python - 如何通过循环读取多个文件作为表(熊猫)并从每个表中选择一列并将其附加在一起