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

Add AsyncStream support for Remote Config

Open peterfriese opened this issue 2 months ago • 5 comments

This PR introduces a new configUpdates property to RemoteConfig, providing an AsyncThrowingStream for consuming real-time config updates. This offers a modern, Swift Concurrency-native alternative to the existing closure-based listener, making it easier to integrate real-time updates into async/await workflows.

Motivation

The existing addOnConfigUpdateListener uses a closure-based callback, which can be cumbersome in modern Swift applications that have adopted Swift Concurrency. By providing an AsyncThrowingStream, we offer a more ergonomic and idiomatic way for developers to receive real-time updates, simplifying their code and improving readability.

Implementation Details

  • FirebaseRemoteConfig/Swift/RemoteConfig+Async.swift: A new file containing an extension on RemoteConfig.

    • It adds a public configUpdates property that returns an AsyncThrowingStream<RemoteConfigUpdate, Error>.
    • The stream's implementation wraps the existing addOnConfigUpdateListener.
    • The listener's lifecycle is automatically managed: it is added when iteration begins and removed when the stream is terminated (e.g., through cancellation).
  • FirebaseRemoteConfig/Tests/Swift/SwiftAPI/AsyncStreamTests.swift: A new test suite for the async stream functionality.

    • Includes mock objects to simulate the real-time service and listener registration.
    • Contains tests for various scenarios, including successful updates, error handling, stream cancellation, and handling of multiple updates.

Example usage

Developers can now listen for real-time updates using a simple for try await loop:

func listenForRealtimeUpdates() {
  Task {
    do {
      for try await configUpdate in remoteConfig.configUpdates {
        print("Updated keys: \(configUpdate.updatedKeys)")

        // Activate the new config to make it available
        let status = try await remoteConfig.activate()
        print("Config activated with status: \(status)")
      }
    } catch {
      print("Error listening for remote config updates: \(error)")
    }
  }
}

This change is purely additive and does not affect any existing APIs.

peterfriese avatar Sep 22 '25 21:09 peterfriese