PaF*_*aFi 0 future publisher firebase swift combine
这就是我正在做的:
-> 使用 FirebaseAuthentification 登录/注册 Firebase
-> 监听 AuthStateDidChangeListenerHandle
-> 我在 Firestore 中存储额外的用户信息,因此我检查 Firestore 中是否存在该用户
-> 如果用户不存在,我创建一个空用户
-> 如果一切成功,我通过回调返回未来发布者(我也想更改它)
这是 checkLoginState 函数:
func checkLoginState(completion: @escaping (AnyPublisher<AccountDetails,Error>) -> Void) {
self.handler = Auth.auth().addStateDidChangeListener { [weak self] auth, user in
guard let safeSelf = self else { return }
completion(Future<AccountDetails,Error> { promise in
if let user = user {
print(user)
print(auth)
safeSelf.checkIfUserIsInDatabase(user: user.uid) { result in
switch result {
case .success(let isAvailable):
if isAvailable {
promise(.success(AccountDetails(userUID: user.uid,name: user.displayName, loggedIn: true, premiumUser: false)))
} else {
safeSelf.createEmptyUser(user: user.uid,email: user.email) { result in
switch result {
case .success(_):
promise(.success(AccountDetails(userUID: user.uid,name: user.displayName, loggedIn: true, premiumUser: false)))
case .failure(let error):
print(error)
}
}
}
case .failure(let error):
print(error)
}
}
} else {
promise(.success(AccountDetails(userUID: nil, loggedIn: false, premiumUser: false)))
}
}.eraseToAnyPublisher()
)
}
}
Run Code Online (Sandbox Code Playgroud)
这些是我当前的功能:
private func checkIfUserIsInDatabase(user id: String, completion: @escaping (Result<Bool,Error>) -> Void)
private func createEmptyUser(user id: String, email:String?, completion: @escaping (Result<Bool,Error>) -> Void)
Run Code Online (Sandbox Code Playgroud)
这就是我想要使用的:
private func checkIfUserIsInDatabase(user id: String) -> AnyPublisher<Bool,Error>
private func createEmptyUser(user id: String) -> AnyPublisher<Bool,Error>
func checkLoginState() -> AnyPublisher<AccountDetails,Error>
Run Code Online (Sandbox Code Playgroud)
我有类似的东西,但它不起作用,也看起来很混乱:
func checkLoginState(completion: @escaping (AnyPublisher<AccountDetails,Error>) -> Void) {
self.handler = Auth.auth().addStateDidChangeListener { [weak self] auth, user in
guard let safeSelf = self else { return }
completion(Future<AccountDetails,Error> { promise in
if let user = user {
print(user)
print(auth)
safeSelf.checkIfUserIsInDatabase(user: user.uid)
.sinkToResult { value in
switch value {
case .success(let isUserInDatabase):
if isUserInDatabase {
promise(.success(AccountDetails(userUID: user.uid,name: user.displayName, loggedIn: true, premiumUser: false)))
} else {
safeSelf.createEmptyUser(user: user.uid)
.sinkToResult { value in
switch value {
case .success( _):
promise(.success(AccountDetails(userUID: user.uid,name: user.displayName, loggedIn: true, premiumUser: false)))
case .failure(let error):
print(error)
}
}
}
case .failure(let error):
print(error)
}
}
} else {
promise(.success(AccountDetails(userUID: nil, loggedIn: false, premiumUser: false)))
}
}.eraseToAnyPublisher()
)
}
}
Run Code Online (Sandbox Code Playgroud)
所以你有某种AccountDetails类型:
import Combine
import FirebaseAuth
struct AccountDetails {
var userId: String
var name: String?
var isLoggedIn: Bool
var isPremiumUser: Bool
}
Run Code Online (Sandbox Code Playgroud)
让我们init用带有 a 的an 来扩展它User,因为它稍后会简化事情:
extension AccountDetails {
init(user: User) {
self.userId = user.uid
self.name = user.displayName
self.isLoggedIn = true
self.isPremiumUser = false
}
}
Run Code Online (Sandbox Code Playgroud)
我认为你的最终目标是Publisher发出AccountDetails. 但由于并不总是有登录用户,因此它应该真正发出Optional<AccountDetails>,以便nil在用户注销时发出。
让我们首先将addStateDidChangeListenerAPI 包装在Publisher. 我们不能Future为此使用 a,因为 aFuture最多发出一个输出,但addStateDidChangeListener可以发出多个事件。所以我们将使用 aCurrentValueSubject来代替。这意味着我们需要一个地方来存储主题和AuthStateDidChangeListenerHandle. 您可以将它们存储为全局变量,或者存储在您的 中AppDelegate,或者您认为合适的任何位置。对于这个答案,让我们创建一个Demo类来保存它们:
class Demo {
static let shared = Demo()
let userPublisher: AnyPublisher<User?, Error>
private let userSubject = CurrentValueSubject<User?, Error>(nil)
private var tickets: [AnyCancellable] = []
private init() {
userPublisher = userSubject.eraseToAnyPublisher()
let handle = Auth.auth().addStateDidChangeListener { [userSubject] (_, user) in
userSubject.send(user)
}
AnyCancellable { Auth.auth().removeStateDidChangeListener(handle) }
.store(in: &tickets)
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在你可以获取Publisher登录用户的 a (如果没有用户登录则为 nil ),如下所示:
let loggedInUserPublisher: AnyPublisher<User?, Error> = Demo.shared.userPublisher
Run Code Online (Sandbox Code Playgroud)
但你真正想要的是一个AccountDetails?出版商,而不是一个User?出版商,像这样:
let accountDetailsPublisher: AnyPublisher<AccountDetails?, Error> = Demo.shared
.accountDetailsPublisher()
Run Code Online (Sandbox Code Playgroud)
所以我们需要编写一个accountDetailsPublisher将 映射User?到 的方法AccountDetails?。
如果User?为零,我们只想发出nil。但如果User?是.some(user),我们需要做更多的异步操作:我们需要检查用户是否在数据库中,如果不在则添加用户。该flatMap运算符允许您链接异步操作,但存在一些复杂性,因为我们需要根据上游发布者的输出采取不同的操作。
我们真的很想隐藏复杂性,只写这样的:
extension Demo {
func loggedInAccountDetailsPublisher() -> AnyPublisher<AccountDetails?, Error> {
return userPublisher
.flatMap(
ifSome: { $0.accountDetailsPublisher().map { Optional.some($0) } },
ifNone: { Just(nil).setFailureType(to: Error.self) })
.eraseToAnyPublisher()
}
}
Run Code Online (Sandbox Code Playgroud)
但接下来我们需要写flatMap(ifSome:ifNone:)。这里是:
extension Publisher {
func flatMap<Wrapped, Some: Publisher, None: Publisher>(
ifSome: @escaping (Wrapped) -> Some,
ifNone: @escaping () -> None
) -> AnyPublisher<Some.Output, Failure>
where Output == Optional<Wrapped>, Some.Output == None.Output, Some.Failure == Failure, None.Failure == Failure
{
return self
.flatMap { $0.map { ifSome($0).eraseToAnyPublisher() } ?? ifNone().eraseToAnyPublisher() }
.eraseToAnyPublisher()
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们需要accountDetailsPublisher在User扩展中实现。这个方法需要做什么?它需要检查是否User在数据库中(一个异步操作),如果没有,则添加User(另一个异步操作)。由于我们需要链接异步操作,因此我们再次需要flatMap. 但我们真的想这样写:
extension User {
func accountDetailsPublisher() -> AnyPublisher<AccountDetails, Error> {
return isInDatabasePublisher()
.flatMap(
ifTrue: { Just(AccountDetails(user: self)).setFailureType(to: Error.self) },
ifFalse: { self.addToDatabase().map { AccountDetails(user: self) } })
}
}
Run Code Online (Sandbox Code Playgroud)
这是flatMap(ifTrue:ifFalse:):
extension Publisher where Output == Bool {
func flatMap<True: Publisher, False: Publisher>(
ifTrue: @escaping () -> True,
ifFalse: @escaping () -> False
) -> AnyPublisher<True.Output, Failure>
where True.Output == False.Output, True.Failure == Failure, False.Failure == Failure
{
return self
.flatMap { return $0 ? ifTrue().eraseToAnyPublisher() : ifFalse().eraseToAnyPublisher() }
.eraseToAnyPublisher()
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们需要在 上编写isInDatabasePublisher和addToDatabase方法User。我没有您的checkIfUserIsInDatabase和createEmptyUser函数的源代码,因此我无法将它们直接转换为发布商。但我们可以使用以下方法包装它们Future:
extension User {
func isInDatabasePublisher() -> AnyPublisher<Bool, Error> {
return Future { promise in
checkIfUserIsInDatabase(user: self.uid, completion: promise)
}.eraseToAnyPublisher()
}
func addToDatabase() -> AnyPublisher<Void, Error> {
return Future { promise in
createEmptyUser(user: self.uid, email: self.email, completion: promise)
} //
.map { _ in } // convert Bool to Void
.eraseToAnyPublisher()
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于您的示例代码忽略了Bool的输出createEmptyUser,因此我改为addToDatabase写入输出Void。
| 归档时间: |
|
| 查看次数: |
1895 次 |
| 最近记录: |