firebase-ios-sdk icon indicating copy to clipboard operation
firebase-ios-sdk copied to clipboard

Add AsyncStream support for Firebase Auth

Open peterfriese opened this issue 2 months ago • 1 comments

Fix #15361

To better align the Firebase Auth SDK with modern Swift concurrency, this PR introduces AsyncStream-based APIs for observing authentication state and ID token changes.

Key Features & Benefits

  • Modern Concurrency: New authStateChanges and idTokenChanges properties on Auth return an AsyncStream<User?>, allowing developers to use for await loops to monitor authentication events, which is more idiomatic in modern Swift.
  • Improved Ergonomics: These new APIs provide a more intuitive and readable alternative to the traditional closure-based listeners (addStateDidChangeListener and addIDTokenDidChangeListener).
  • Resource Safety: The streams are designed to be safely cancelled, preventing potential resource leaks. This is validated by comprehensive unit tests.

Implementation Details

  • The new async properties are housed in a new FirebaseAuth/Sources/Auth+Async.swift file.
  • Unit tests have been added in FirebaseAuth/Tests/Unit/AuthAsyncTests.swift to cover sign-in, sign-out, token refresh, and stream cancellation scenarios.
  • The FirebaseAuth/CHANGELOG.md has been updated to reflect these new features.
  • The new APIs are documented with clear usage examples, following Apple's documentation best practices.

Example Usage

Monitoring Authentication State

func monitorAuthState() async {
  for await user in Auth.auth().authStateChanges {
    if let user = user {
      print("User signed in: \(user.uid)")
      // Update UI or perform actions for a signed-in user.
    } else {
      print("User signed out.")
      // Update UI or perform actions for a signed-out state.
    }
  }
}

Monitoring ID Token Changes

func monitorIDTokenState() async {
  for await user in Auth.auth().idTokenChanges {
    if let user = user {
      print("ID token changed for user: \(user.uid)")
      // The user's ID token has been refreshed.
      // You can get the new token by calling user.getIDToken()
      do {
        let token = try await user.getIDToken()
        print("New ID token: \(token)")
        // Send the new token to your backend server, etc.
      } catch {
        print("Error getting ID token: \(error)")
      }
    }
  }
}

peterfriese avatar Sep 28 '25 14:09 peterfriese