代码之家  ›  专栏  ›  技术社区  ›  Zach Marion

使用SwiftUI、Combine和Firebase,在将用户的帐户链接到电子邮件/密码之前,我如何验证用户是否匿名登录?

  •  0
  • Zach Marion  · 技术社区  · 3 年前

    我有一个具有以下功能的AuthService:

    func signInAnon() -> AnyPublisher<User, AuthError> {
        return Future<User, AuthError> { promise in
            Auth.auth().signInAnonymously { result, error in
                if let error = error {
                    return promise(.failure(.signInAnon(description: error.localizedDescription)))
                } else if let user = result?.user {
                    return promise(.success(user))
                }
            }
        }.eraseToAnyPublisher()
    }
    
    func linkAccount(email: String, password: String) -> AnyPublisher<User, AuthError> {
        
        let emailCredential = EmailAuthProvider.credential(withEmail: email, password: password)
        
        return Future<User, AuthError> { promise in
            Auth.auth().currentUser?.link(with: emailCredential) { result, error in
                if let error = error {
                    return promise(.failure(.linkAccount(description: error.localizedDescription)))
                } else if let user = result?.user {
                    Auth.auth().updateCurrentUser(user) { error in
                        if let error = error {
                            return promise(.failure(.updateCurrentUser(description: error.localizedDescription)))
                        } else {
                            return promise(.success((user)))
                        }
                    }
                }
            }
        } .eraseToAnyPublisher()
    }
    

    在ViewModel中,我想将用户的匿名帐户与电子邮件/密码凭据链接起来。然而,在登录时,匿名登录尚未发生。

    以下是我ViewModel中的代码:

    case .signUp:
    
    isLoading = true
    
    authService.signInAnon()
        .timeout(.seconds(5), scheduler: DispatchQueue.main, options: nil, customError: { () -> AuthError in
            return .signInAnon(description: self.error?.localizedDescription)})
        .sink { completion in
            switch completion {
            case let .failure(error):
                self.error = error
            case .finished:
                print("User signed in anonymously")
                self.error = nil
                }
        } receiveValue: { user in
            self.user = user
        }
        .store(in: &cancellables)
    
    authService.linkAccount(email: email, password: password)
        .timeout(.seconds(5), scheduler: DispatchQueue.main, options: nil, customError: { () -> AuthError in
            return .linkAccount(description: self.error?.localizedDescription)})
        .sink { completion in
        self.isLoading = false
        switch completion {
        case let .failure(error):
            self.error = error
            self.alertIsPresented = true
            print(error.localizedDescription)
        case .finished:
            print("Sign up successful")
        }
    } receiveValue: { user in
        self.user = user
    }
    .store(in: &cancellables)
    

    我遇到了问题,因为有时链接帐户的函数会在匿名登录函数之前完成,从而出现错误。

    我如何有条件地写这个,以便检查用户是否匿名登录?如果是,请链接他们的帐户。如果他们没有,请匿名登录,然后链接他们的帐户。

    0 回复  |  直到 3 年前
        1
  •  0
  •   Peter Friese    3 年前

    以下是我对如何使用Firebase匿名身份验证的建议:

    1. 一旦你的应用程序启动,初始化Firebase并检查 用户已登录。如果用户之前已登录,Firebase Auth将在您调用此方法之前及时获取该用户。
    2. 如果没有用户,请匿名登录。
    3. 然后,您可以开始使用受益于登录用户的所有Firebase服务(如Firestore)
    4. 一旦用户决定实际登录,使用任何Firebase身份验证提供程序获取令牌,并将其链接到现有的匿名用户。顺便说一句,我建议使用比电子邮件/密码更安全的东西。

    在应用程序启动时执行匿名登录的示例代码(取自 this sample app ):

    func signIn() {
      if Auth.auth().currentUser == nil {
        Auth.auth().signInAnonymously()
      }
    }
    

    升级到Apple帐户登录的示例代码(取自 the same sample app ):

          case .link:
            if let currentUser = Auth.auth().currentUser {
              currentUser.link(with: credential) { (result, error) in
                if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
                  print("The user you're signing in with has already been linked, signing in to the new user and migrating the anonymous users [\(currentUser.uid)] tasks.")
                  
                  if let updatedCredential = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
                    print("Signing in using the updated credentials")
                    Auth.auth().signIn(with: updatedCredential) { (result, error) in
                      if let user = result?.user {
                        currentUser.getIDToken { (token, error) in
                          if let idToken = token {
                            (self.taskRepository as? FirestoreTaskRepository)?.migrateTasks(from: idToken)
                            self.doSignIn(appleIDCredential: appleIDCredential, user: user)
                          }
                        }
                      }
                    }
                  }
                }
                else if let error = error {
                  print("Error trying to link user: \(error.localizedDescription)")
                }
                else {
                  if let user = result?.user {
                    self.doSignIn(appleIDCredential: appleIDCredential, user: user)
                  }
                }
              }
            }
    

    如果您对Combine的情况感兴趣,我们已经开始为Firebase实现Combine支持-查看我们的 project tracker