SwiftUI中Firebase当前登录用户获取、登录成功页面跳转及登录状态持久化问题求助(使用Combine框架)
Hey there! I've put together a complete implementation that covers all your requirements—fetching the current logged-in user, switching views on successful login, and keeping the login state persistent even after closing the app. We'll use Combine to handle all reactive state changes cleanly.
1. First: Make Sure Firebase Auth is Set Up
Before diving into code, confirm you’ve got Firebase configured in your SwiftUI project: added the GoogleService-Info.plist, installed FirebaseAuth via Swift Package Manager, and initialized Firebase in your app’s entry point. That’s our foundation.
2. Auth View Model with Combine
This view model will manage all auth logic, publish user state changes, and handle login using Combine. It wraps Firebase’s auth state listener into a reactive publisher for our views to observe.
import Foundation import Combine import FirebaseAuth class AuthViewModel: ObservableObject { // Published property to notify views of user state changes @Published var currentUser: User? // Store Combine subscriptions to avoid memory leaks private var cancellables = Set<AnyCancellable>() init() { // Listen to Firebase auth state changes and bind to currentUser authStatePublisher .receive(on: DispatchQueue.main) .assign(to: \.currentUser, on: self) .store(in: &cancellables) } // Combine publisher wrapping Firebase's auth state listener private var authStatePublisher: AnyPublisher<User?, Never> { Deferred { Future { promise in // Firebase's built-in listener tracks auth state (login, logout, app restart) let _ = Auth.auth().addStateDidChangeListener { _, user in promise(.success(user)) } } } .eraseToAnyPublisher() } // Email/password login method using Combine func login(email: String, password: String) -> AnyPublisher<User, Error> { Future { promise in Auth.auth().signIn(withEmail: email, password: password) { result, error in if let error = error { promise(.failure(error)) } else if let user = result?.user { promise(.success(user)) } } } .receive(on: DispatchQueue.main) .eraseToAnyPublisher() } // Optional logout method func logout() { do { try Auth.auth().signOut() } catch { print("Logout error: \(error.localizedDescription)") } } }
Key Details:
authStatePublisher: Turns Firebase’s imperative state listener into a Combine publisher. It emits the current user whenever auth state changes (including on app launch if a session is persisted).@Published var currentUser: Views observe this property to reactively switch between login and main content.- Firebase’s Built-in Persistence: Firebase Auth automatically saves user sessions by default—no extra work needed here. The listener will pick up the saved user immediately when the app restarts.
3. SwiftUI Views: Login & Main Content
We’ll create two views, and let the auth state drive which one is displayed.
Login View
import SwiftUI struct LoginView: View { @ObservedObject var authViewModel: AuthViewModel @State private var email = "" @State private var password = "" @State private var errorMessage: String? var body: some View { VStack(spacing: 20) { TextField("Email", text: $email) .textFieldStyle(.roundedBorder) .autocapitalization(.none) SecureField("Password", text: $password) .textFieldStyle(.roundedBorder) Button("Login") { authViewModel.login(email: email, password: password) .sink(receiveCompletion: { completion in if case .failure(let error) = completion { errorMessage = error.localizedDescription } }, receiveValue: { _ in // No manual view switch needed—root view handles it automatically }) .store(in: &authViewModel.cancellables) } .buttonStyle(.borderedProminent) if let errorMessage = errorMessage { Text(errorMessage) .foregroundColor(.red) } } .padding() .navigationTitle("Login") } }
Main View
import SwiftUI struct MainView: View { @ObservedObject var authViewModel: AuthViewModel var body: some View { VStack(spacing: 20) { Text("Welcome, \(authViewModel.currentUser?.email ?? "User")!") .font(.title) Button("Logout") { authViewModel.logout() } .buttonStyle(.bordered) } .padding() .navigationTitle("Home") } }
4. App Root: Switch Views Based on Auth State
The root of our app will observe the currentUser property and automatically switch between login and main content.
import SwiftUI @main struct FirebaseAuthCombineApp: App { // Single instance of AuthViewModel for the entire app private let authViewModel = AuthViewModel() var body: some Scene { WindowGroup { NavigationStack { if authViewModel.currentUser != nil { MainView(authViewModel: authViewModel) } else { LoginView(authViewModel: authViewModel) } } } } }
How It All Comes Together:
- On app launch, the
AuthViewModelinitializes and starts listening to Firebase’s auth state. If a user was previously logged in,currentUseris set immediately, and the app showsMainView. - When login succeeds, the auth state listener emits the new user, updating
currentUserand triggering the root view to switch toMainView. - On logout,
currentUserbecomesnil, and the root view switches back toLoginView.
5. Test Persistence
To confirm persistence works:
- Log in with valid credentials.
- Close the app completely (swipe it away from the app switcher).
- Reopen the app—you’ll be automatically logged in and taken straight to
MainView.
That’s all! This implementation checks all your boxes using Combine and Firebase’s native features. Let me know if you need tweaks for your specific use case.
内容的提问来源于stack exchange,提问作者Lucky

