在一个不相关的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()
}
}

Profile页面本应获取该信息并从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类的实例方法addStateDidChangeListener(_:),并将通过完成处理程序传入的User实例分配给AuthenticationState中您自己的loggedInUser属性。这样,每当用户登录或注销时,您都会收到通知——保持同步。像这样:

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:)方法将UserProfileViewModelloggedInUser属性绑定到AuthenticationState的:,而不是在init()期间进行一次性赋值(如当前所述(

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需要存在多长时间,都要强烈提及这一点。

最新更新