SwiftUI Firebase身份验证,在网络连接丢失后保留用户

nkoocmlb  于 2023-02-25  发布在  Swift
关注(0)|答案(2)|浏览(139)

目前我正在实现一个应用程序并使用Firebase身份验证,我希望在连接丢失时弹出一个窗口,当我切断网络连接时,应用程序会将用户注销,这是我不希望的,而且Xcode无法识别auth.setPersistence(.local)方法
这是我的内容视图,用于检查用户是否已登录:

struct ContentView: View {
    @EnvironmentObject var viewModel: AppViewModel
    
    var body: some View {
        NavigationView{
            if viewModel.loggedIn{
                HomeView()
            } else {
            LoginView()
            }
        }
        .onAppear {
           viewModel.loggedIn = viewModel.isSignedIN
         }
    }
}

loggedIn变量声明方式如下:

@Published var loggedIn = false

以下是用于登录、注册和注销的方法:

/// This function connects to the Firebase Authentication database and logs the user in
    /// - Parameters:
    ///   - email: e-mail address entered by user in order to log in
    ///   - password: password entered by user in order to log in
    func logIn(email: String, password: String) async throws {
        mail = email
        let authRes = try await auth.signIn(withEmail: email, password: password)
        loggedIn = true
    }
    
    
    /// This function signs the user up
    /// - Parameters:
    ///   - email: e-mail used for signing up
    ///   - password: password used for signing up
    func signUp(email: String, password: String) async throws {
        mail = email
        let authRes = try await auth.createUser(withEmail: email, password: password)
        loggedIn = true
    }
    
    
    /// This function logs the user out
    func logOut() {
        try? auth.signOut()
        self.loggedIn = false
        self.eventlist.removeAll()
    }

我尝试将loggedIn变量保存在用户默认值中,但不幸的是它不起作用
有人知道处理这个问题的最佳方法是什么吗?

wmvff8tz

wmvff8tz1#

而不是创建自己的逻辑来维护登录状态,您可以使用。

handle = Auth.auth().addStateDidChangeListener { auth, user in
  isLoggedIn = user != nil
}

https://firebase.google.com/docs/auth/ios/start
然后创建

@AppStorage var isloggedIn:Bool = false

这将保存用户的真实状态,您不必也不应该以任何其他方式更改此变量。
此外,请确保删除不使用的侦听器,例如deinit

Auth.auth().removeStateDidChangeListener(handle!)
shyt4zoc

shyt4zoc2#

不需要自己实现这一点(在任何情况下都不要在UserDefaults中存储任何敏感信息,因为这是不安全的),因为Firebase SDK会为您处理这一问题。
实际上,我们将登录用户存储在Keychain中,并在示例化Auth对象时立即检索它。
因此,您只需在应用加载完成后初始化Firebase Auth,然后设置一个身份验证状态侦听器,以便在用户登录或注销时收到通知。
以下是AuthenticationViewModel的代码片段,显示了如何在SwiftUI中执行此操作:

@MainActor
class AuthenticationViewModel: ObservableObject {
  @Published var email = ""
  @Published var password = ""
  @Published var confirmPassword = ""

  @Published var authenticationState: AuthenticationState = .unauthenticated
  @Published var errorMessage = ""
  @Published var user: User?
  @Published var displayName = ""

  init() {
    registerAuthStateHandler()
  }

  private var authStateHandler: AuthStateDidChangeListenerHandle?

  func registerAuthStateHandler() {
    if authStateHandler == nil {
      authStateHandler = Auth.auth().addStateDidChangeListener { auth, user in
        self.user = user
        self.authenticationState = user == nil ? .unauthenticated : .authenticated
        self.displayName = user?.email ?? ""
      }
    }
  }

}

// MARK: - Email and Password Authentication
extension AuthenticationViewModel {
  func signInWithEmailPassword() async -> Bool {
    authenticationState = .authenticating
    do {
      try await Auth.auth().signIn(withEmail: self.email, password: self.password)
      return true
    }
    catch  {
      print(error)
      errorMessage = error.localizedDescription
      authenticationState = .unauthenticated
      return false
    }
  }

  func signUpWithEmailPassword() async -> Bool {
    authenticationState = .authenticating
    do  {
      try await Auth.auth().createUser(withEmail: email, password: password)
      return true
    }
    catch {
      print(error)
      errorMessage = error.localizedDescription
      authenticationState = .unauthenticated
      return false
    }
  }

  func signOut() {
    do {
      try Auth.auth().signOut()
    }
    catch {
      print(error)
      errorMessage = error.localizedDescription
    }
  }
}

该项目还包含一个AuthenticatedView,它将根据身份验证状态显示不同的视图:

struct AuthenticatedView<Content, Unauthenticated>: View where Content: View, Unauthenticated: View {
  @StateObject private var viewModel = AuthenticationViewModel()
  @State private var presentingLoginScreen = false
  @State private var presentingProfileScreen = false

  var unauthenticated: Unauthenticated?
  @ViewBuilder var content: () -> Content

  public init(unauthenticated: Unauthenticated?, @ViewBuilder content: @escaping () -> Content) {
    self.unauthenticated = unauthenticated
    self.content = content
  }

  public init(@ViewBuilder unauthenticated: @escaping () -> Unauthenticated, @ViewBuilder content: @escaping () -> Content) {
    self.unauthenticated = unauthenticated()
    self.content = content
  }

  var body: some View {
    switch viewModel.authenticationState {
    case .unauthenticated, .authenticating:
      VStack {
        if let unauthenticated {
          unauthenticated
        }
        else {
          Text("You're not logged in.")
        }
        Button("Tap here to log in") {
          viewModel.reset()
          presentingLoginScreen.toggle()
        }
      }
      .sheet(isPresented: $presentingLoginScreen) {
        AuthenticationView()
          .environmentObject(viewModel)
      }
    case .authenticated:
      VStack {
        content()
        Text("You're logged in as \(viewModel.displayName).")
        Button("Tap here to view your profile") {
          presentingProfileScreen.toggle()
        }
      }
      .sheet(isPresented: $presentingProfileScreen) {
        NavigationView {
          UserProfileView()
            .environmentObject(viewModel)
        }
      }
    }
  }
}

这些文档展示了如何在Swift和Objective-C中实现这一点,如果你想看看如何实现这一点,请查看我的一个Firebase Auth视频,其中我展示了如何在SwiftUI中实现这一点:

相关问题