You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中Firebase当前登录用户获取、登录成功页面跳转及登录状态持久化问题求助(使用Combine框架)

SwiftUI + Firebase Auth with Combine: User Persistence & View Switching

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:

  1. On app launch, the AuthViewModel initializes and starts listening to Firebase’s auth state. If a user was previously logged in, currentUser is set immediately, and the app shows MainView.
  2. When login succeeds, the auth state listener emits the new user, updating currentUser and triggering the root view to switch to MainView.
  3. On logout, currentUser becomes nil, and the root view switches back to LoginView.

5. Test Persistence

To confirm persistence works:

  1. Log in with valid credentials.
  2. Close the app completely (swipe it away from the app switcher).
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 09:22:28