feat: add session replay support for react native#357
feat: add session replay support for react native#357mario-launchdarkly wants to merge 57 commits intomainfrom
Conversation
3e1346c to
8b1fb92
Compare
sdk/@launchdarkly/react-native-ld-session-replay/android/src/main/AndroidManifest.xml
Dismissed
Show dismissed
Hide dismissed
...launchdarkly/react-native-ld-session-replay/example/android/app/src/main/AndroidManifest.xml
Dismissed
Show dismissed
Hide dismissed
...native-ld-session-replay/example/android/app/src/main/res/drawable/rn_edit_text_material.xml
Dismissed
Show dismissed
Hide dismissed
...native-ld-session-replay/example/android/app/src/main/res/drawable/rn_edit_text_material.xml
Dismissed
Show dismissed
Hide dismissed
...-replay/android/src/main/java/com/sessionreplayreactnative/SessionReplayReactNativeModule.kt
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayReactNative.mm
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayReactNative.mm
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayReactNative.mm
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/SessionReplayReactNative.podspec
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
73e0355 to
cbb094f
Compare
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/react-native-ld-session-replay/ios/SessionReplayAdapter.swift
Outdated
Show resolved
Hide resolved
- Removed the static method for closing the LDClient singleton when a client is deallocated during initialization to avoid tearing down a new client's session replay. - Updated comments to clarify the handling of client replacement scenarios, ensuring that the LDClient remains consistent and functional during initialization.
- Added detailed usage examples for integrating the session replay plugin with the LaunchDarkly React Native client. - Included both the declarative and imperative API approaches for configuring and starting session replay, enhancing documentation clarity for developers.
…onReplayAdapter - Changed the client state update method from asynchronous to synchronous to ensure thread safety when modifying the client state. - This adjustment prevents potential race conditions and improves the reliability of client state management.
- Added @launchdarkly/react-native-client-sdk version 10.12.5 to dependencies. - Updated import statement for session replay plugin to use the scoped package @launchdarkly/session-replay-react-native.
- Updated the session replay plugin import to use the scoped package @launchdarkly/session-replay-react-native for better compatibility. - Ensured that the example project dependencies are aligned with the latest version of the LaunchDarkly SDK.
…= true (line 86) outside the clientQueue
… better readability
…apter - Refactored state handling in start and stop methods to ensure proper synchronization and prevent race conditions. - Enhanced logic to manage ldReplayState transitions more clearly, ensuring that stopping and starting behaviors are correctly handled in a thread-safe manner.
- Updated the state handling to set ldReplayState to .starting before invoking LDClient.start(), ensuring accurate detection of in-flight start and stop processes. - Removed redundant state assignment within the completion handler to streamline the logic and enhance clarity.
…mpletion callback
a54e553 to
c0cf2e0
Compare
| self?.isLDClientState = .started | ||
| self?.setLDReplayEnabled(true) { | ||
| /// offline is considered a short circuited timed out case | ||
| completion(true, nil) |
There was a problem hiding this comment.
Replay ignores disable option
Medium Severity
SessionReplayClientAdapter.start always sets LDReplay.shared.isEnabled to true, so options.isEnabled is effectively ignored after configuration. Calls that explicitly pass isEnabled: false still start capture, causing unexpected replay collection.
Additional Locations (2)
| /// offline is considered a short circuited timed out case | ||
| setLDReplayEnabled(true) { | ||
| completion(true, nil) | ||
| } |
There was a problem hiding this comment.
Reconfigure has no effect after start
Medium Severity
After the first successful start, later configure calls update stored mobileKey and sessionReplayOptions, but start in the .started state only toggles LDReplay.shared.isEnabled and never rebuilds LDClient with new settings. This causes silent stale configuration at runtime.
Additional Locations (1)
| BuildableName = "SessionReplayReactNativeExampleTests.xctest" | ||
| BlueprintName = "SessionReplayReactNativeExampleTests" | ||
| ReferencedContainer = "container:SessionReplayReactNativeExample.xcodeproj"> | ||
| </BuildableReference> |
There was a problem hiding this comment.
Broken iOS test scheme reference
Low Severity
The scheme’s TestAction references SessionReplayReactNativeExampleTests, but that target is not defined in project.pbxproj. Running tests from this shared scheme can fail because the BlueprintIdentifier points to a non-existent test bundle.
Additional Locations (1)
| BuildableName = "SessionReplayReactNativeExampleTests.xctest" | ||
| BlueprintName = "SessionReplayReactNativeExampleTests" | ||
| ReferencedContainer = "container:SessionReplayReactNativeExample.xcodeproj"> | ||
| </BuildableReference> |
There was a problem hiding this comment.
iOS scheme references missing test target
Medium Severity
The shared Xcode scheme includes SessionReplayReactNativeExampleTests, but the project file defines only the app target. Running the scheme’s test action points to a non-existent target and can fail CI or local xcodebuild test runs for the example app.
Additional Locations (1)
| self?.setLDReplayEnabled(true) { | ||
| /// offline is considered a short circuited timed out case | ||
| completion(true, nil) | ||
| } |
There was a problem hiding this comment.
Stop can be undone by pending start
Medium Severity
start always enables LDReplay in the async LDClient.start completion, but stop only toggles LDReplay immediately and does not cancel or gate pending starts. If stop is called while startup is in progress, replay can be re-enabled afterward, leaving recording active against the latest user action.
Additional Locations (1)
| self?.isLDClientState = .started | ||
| self?.setLDReplayEnabled(true) { | ||
| /// offline is considered a short circuited timed out case | ||
| completion(true, nil) |
There was a problem hiding this comment.
isEnabled option is effectively ignored
Medium Severity
The adapter parses options.isEnabled, but start always forces LDReplay.shared.isEnabled to true. This overrides disabled configurations and makes isEnabled: false ineffective once startSessionReplay is called.


Summary
Implement support for session replay into react native through swift-observability-sdk
How did you test this change?
e2e testing, example app is provided
Are there any deployment considerations?
it is a front-end package for building mobile apps
Note
Medium Risk
Adds new mobile SDK surface area and native iOS bridging to session replay/observability plugins, which could affect app startup behavior and recording privacy options; Android is explicitly non-functional (rejects calls).
Overview
Adds a new
@launchdarkly/session-replay-react-nativepackage that exposes a TurboModule-backed API (configureSessionReplay,startSessionReplay,stopSessionReplay) and anLDPluginadapter (createSessionReplayPlugin) to auto-initialize from LaunchDarkly client metadata.Implements the native bridge primarily on iOS via
SessionReplayClientAdapterwiring upLaunchDarklyObservability+LaunchDarklySessionReplayand enabling/disablingLDReplay; Android is scaffolded but currently rejects calls as not supported. Includes a full React Native example app and build tooling/config (Gradle/Xcode/Pods, Turbo tasks, lint/typecheck hooks), plus repo-level updates like pinningturboto2.7.6and running Vitest in CI mode.Written by Cursor Bugbot for commit be3ee93. This will update automatically on new commits. Configure here.