Skip to content

feat: add session replay support for react native#357

Open
mario-launchdarkly wants to merge 57 commits intomainfrom
feature/react-native-swift-session-replay
Open

feat: add session replay support for react native#357
mario-launchdarkly wants to merge 57 commits intomainfrom
feature/react-native-swift-session-replay

Conversation

@mario-launchdarkly
Copy link

@mario-launchdarkly mario-launchdarkly commented Jan 27, 2026

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-native package that exposes a TurboModule-backed API (configureSessionReplay, startSessionReplay, stopSessionReplay) and an LDPlugin adapter (createSessionReplayPlugin) to auto-initialize from LaunchDarkly client metadata.

Implements the native bridge primarily on iOS via SessionReplayClientAdapter wiring up LaunchDarklyObservability + LaunchDarklySessionReplay and enabling/disabling LDReplay; 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 pinning turbo to 2.7.6 and running Vitest in CI mode.

Written by Cursor Bugbot for commit be3ee93. This will update automatically on new commits. Configure here.

@mario-launchdarkly mario-launchdarkly requested a review from a team as a code owner January 27, 2026 20:27
@mario-launchdarkly mario-launchdarkly force-pushed the feature/react-native-swift-session-replay branch from 3e1346c to 8b1fb92 Compare January 27, 2026 20:38
@Vadman97 Vadman97 force-pushed the feature/react-native-swift-session-replay branch from 73e0355 to cbb094f Compare February 9, 2026 18:44
mario-launchdarkly and others added 26 commits February 13, 2026 16:58
- 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.
…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.
@Vadman97 Vadman97 force-pushed the feature/react-native-swift-session-replay branch from a54e553 to c0cf2e0 Compare February 13, 2026 22:59
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

self?.isLDClientState = .started
self?.setLDReplayEnabled(true) {
/// offline is considered a short circuited timed out case
completion(true, nil)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

/// offline is considered a short circuited timed out case
setLDReplayEnabled(true) {
completion(true, nil)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

BuildableName = "SessionReplayReactNativeExampleTests.xctest"
BlueprintName = "SessionReplayReactNativeExampleTests"
ReferencedContainer = "container:SessionReplayReactNativeExample.xcodeproj">
</BuildableReference>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

BuildableName = "SessionReplayReactNativeExampleTests.xctest"
BlueprintName = "SessionReplayReactNativeExampleTests"
ReferencedContainer = "container:SessionReplayReactNativeExample.xcodeproj">
</BuildableReference>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

self?.setLDReplayEnabled(true) {
/// offline is considered a short circuited timed out case
completion(true, nil)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

self?.isLDClientState = .started
self?.setLDReplayEnabled(true) {
/// offline is considered a short circuited timed out case
completion(true, nil)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants