Skip to content

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 native iOS code that manages LaunchDarkly’s singleton LDClient lifecycle and session replay privacy settings; incorrect configuration or lifecycle handling could cause resource leaks or unexpected replay behavior.

Overview
Adds a new React Native TurboModule package, session-replay-react-native, exposing configureSessionReplay, startSessionReplay, and stopSessionReplay, plus a LaunchDarkly LDPlugin adapter (createSessionReplayPlugin) that auto-initializes replay using the client’s metadata.sdkKey/mobileKey.

Implements the iOS native bridge via SessionReplayAdapter and an Objective-C++ module, wiring into LaunchDarklyObservability/LaunchDarklySessionReplay, mapping privacy/masking options, and adding lifecycle handling to avoid singleton LDClient leaks and handle stop-during-start. Android is scaffolded but explicitly returns NOT_SUPPORTED for all APIs.

Includes a full example app (iOS + Android projects) demonstrating usage, and adds repo/tooling configs (ESLint/Prettier/TypeScript, commitlint + lefthook hooks, turbo tasks, podspec/SPM dependency, docs/license).

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

@mario-launchdarkly mario-launchdarkly force-pushed the feature/react-native-swift-session-replay branch from 3e1346c to 8b1fb92 Compare January 27, 2026 20:38
…n start rejection

- Implemented `configure` and `startSessionReplay` methods in `SessionReplayReactNativeModule.kt` to handle session replay functionality.
- Both methods currently reject promises with a "NOT_SUPPORTED" message, indicating that session replay is not yet supported on Android, while iOS support is available.
…eplay configuration

- Removed unnecessary console log statements from `configureSessionReplay` and `SessionReplayPluginAdapter`.
- Enhanced error handling by silencing configuration failure messages, allowing the native module to manage errors.
mario-launchdarkly and others added 3 commits January 27, 2026 16:48
- Changed plugin name from '@launchdarkly/observability-react-native' to 'session-replay-react-native'.
- Removed try/catch block around async session replay configuration to simplify error handling, allowing the native module to manage errors directly.
- Changed context declaration to be optional and added logging for context creation failure.
- Updated the start method to guard against nil context, ensuring session replay does not start without a valid context.
- Added a stop method to the Client class to disable session replay via LDReplay.shared.
- Updated the stop method in SessionReplayAdapter to call the new stop method on the client, ensuring proper session replay termination.
- Updated the stop method in SessionReplayAdapter to mutate LDReplay.shared.isEnabled on the main actor, ensuring thread safety when stopping session replay.
- Updated the startSessionReplay method in SessionReplayReactNative to handle success and failure cases using a completion block.
- Modified the start method in SessionReplayAdapter to accept a completion handler, providing better error reporting when the client is not initialized or when session replay fails to start.
- Improved error logging for session replay initialization issues, ensuring clearer diagnostics for developers.
…yAdapter

- Eliminated the sessionReplayOptions variable from SessionReplayAdapter as it was not utilized, streamlining the code and improving clarity.
…EADME

- Updated the README to include steps for configuring the LaunchDarkly mobile key using environment variables or direct replacement in the code.
- Added .env files to .gitignore to prevent committing sensitive information.
- Modified App.tsx to utilize the mobile key from an environment variable, enhancing security and flexibility.
- Updated the glob pattern in the lefthook.yml file to remove unnecessary spaces, ensuring proper file matching for pre-commit hooks.
- Eliminated the NSLog statement that logged the mobile key and options during configuration in SessionReplayReactNative, reducing unnecessary console output and improving code cleanliness.
- Removed commented-out code related to the Client initialization and SessionReplay options, enhancing code readability and maintainability.
- Implemented stopSessionReplay method in both iOS and Android modules to allow stopping of session replay.
- Updated TypeScript interface to include stopSessionReplay, enhancing the API for session management.
- Added error handling for the stopSessionReplay method on Android, indicating that session replay is not yet supported.
- Updated setMobileKey method to accept optional options dictionary, improving flexibility in configuration.
- Modified sessionReplayOptionsFrom method to handle nil dictionary, providing default values for session replay options, ensuring robust behavior when no options are provided.
- Enhanced error handling in the SessionReplayPluginAdapter by logging initialization failures to the console, providing clearer diagnostics for developers when session replay fails to start.
- Added logic to close the previous LDClient instance before initializing a new one in setMobileKey and start methods, ensuring proper resource management.
- Implemented a close method in the Client class to handle the shutdown of LDClient, preventing potential memory leaks and ensuring clean termination of network connections and background tasks.
- Introduced synchronization mechanisms to prevent concurrent start calls and manage the LDClient lifecycle more effectively.
- Added flags to handle pending close requests when the client is starting, ensuring proper resource management and preventing potential memory leaks.
- Enhanced error logging during session replay initialization to provide clearer diagnostics for developers.
- Refactored the handling of pending close requests for LDClient to ensure proper resource management when session replay is starting.
- Introduced a dedicated method to close the pending client, enhancing clarity and maintaining the integrity of the session replay lifecycle.
- Improved logging to provide better diagnostics when a close is requested during initialization.
…tialization

- Added logic to clean up the LDClient singleton if the client is deallocated while starting, preventing resource leaks.
- Improved error handling by providing a completion callback with a message when the client is replaced during initialization.
- Enhanced logging to indicate when the LDClient is closed due to client replacement, improving diagnostics for developers.
- Added trimming and validation for the mobile key in both SessionReplayAdapter and SessionReplayReactNative to ensure it is non-empty before proceeding with initialization.
- Enhanced error handling to reject invalid mobile keys with appropriate messages, improving user feedback during configuration.
- Updated the TypeScript interface to enforce mobile key validation, ensuring robust session replay setup.
- Eliminated the unused Hook type import from the SessionReplayPluginAdapter, streamlining the code and improving clarity.
- Cleaned up the codebase by removing the getHooks method, which was not utilized, enhancing maintainability.
- Introduced a static method to handle the cleanup of the LDClient singleton when the client is deallocated during initialization, preventing inconsistent states.
- Enhanced the error handling logic to ensure proper closure of the LDClient and improved logging for diagnostics when a client is replaced.
- 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.
# runner: node
#
# - script: "hello.go"
# runner: go run
Copy link

Choose a reason for hiding this comment

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

Root lefthook.yml is entirely commented-out dead code

Low Severity

The root lefthook.yml consists entirely of commented-out example configuration (42 lines of comments). It provides no actual functionality. The package has its own working lefthook configuration at sdk/@launchdarkly/react-native-ld-session-replay/lefthook.yml. This file should either be removed or contain actual configuration.

Fix in Cursor Fix in Web

- 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.
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.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

if shouldClosePending, let ldClient = LDClient.get() {
self.closePendingClient(ldClient: ldClient)
}
completion(false, error)
Copy link

Choose a reason for hiding this comment

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

Timeout error handling is unreachable dead code

Medium Severity

The timeout error handling at line 204 checks self.config.startOnline == true, but config.startOnline is hardcoded to false at line 146. This condition can never be satisfied, so even when timedOut is true, the code falls through to the else branch and reports success. Session replay initialization timeouts are silently ignored and reported as successful starts.

Additional Locations (1)

Fix in Cursor Fix in Web

set {
clientStateQueue.async(flags: .barrier) { [weak self] in self?._clientState = newValue }
}
}
Copy link

Choose a reason for hiding this comment

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

Race condition from async setter with sync getter

Medium Severity

The clientState computed property uses async(flags: .barrier) for the setter but sync for the getter. A write followed immediately by a read may return the stale value because the async write hasn't completed yet. This affects the check at line 38 in setMobileKey where clientState == .started may read outdated state, causing the previous client not to be closed properly.

Additional Locations (1)

Fix in Cursor Fix in Web

# runner: node
#
# - script: "hello.go"
# runner: go run
Copy link

Choose a reason for hiding this comment

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

Entirely commented-out configuration file is dead code

Low Severity

The root lefthook.yml contains only commented-out example configuration with no actual hooks defined. The package already has a working lefthook.yml at sdk/@launchdarkly/react-native-ld-session-replay/lefthook.yml with actual pre-commit hooks. This file serves no purpose and should either be configured or removed.

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.

4 participants