Skip to content

Commit 1bb13eb

Browse files
authored
chore: Add FDv2 example project for react-native. (#1277)
<!-- CURSOR_SUMMARY --> > [!NOTE] > **Low Risk** > Low risk: changes are isolated to adding a new React Native example app and updating workspace/release tooling references, without modifying SDK runtime behavior. > > **Overview** > Adds a new `packages/sdk/react-native/example-fdv2` Expo app workspace that demonstrates the experimental FDv2 `dataSystem` opt-in, context identification, boolean flag evaluation, and manual connection-mode switching via internal APIs. > > Updates monorepo configuration to include the new workspace (`package.json`), exclude it from the React Native SDK TypeScript build (`packages/sdk/react-native/tsconfig.json`), and ensure `release-please` keeps the example’s `@launchdarkly/react-native-client-sdk` dependency in sync (`release-please-config.json`). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c895665. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/launchdarkly/js-core/pull/1277" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review"> </picture> </a> <!-- devin-review-badge-end -->
1 parent 4818678 commit 1bb13eb

15 files changed

Lines changed: 410 additions & 0 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"packages/sdk/react/examples/vercel-edge",
2626
"packages/sdk/react-native",
2727
"packages/sdk/react-native/example",
28+
"packages/sdk/react-native/example-fdv2",
2829
"packages/sdk/react-native/contract-tests/entity",
2930
"packages/sdk/vercel",
3031
"packages/sdk/svelte",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
LAUNCHDARKLY_MOBILE_KEY=
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2+
.env
3+
4+
# dependencies
5+
node_modules/
6+
7+
# Expo
8+
.expo/
9+
dist/
10+
web-build/
11+
12+
# Native
13+
*.orig.*
14+
*.jks
15+
*.p8
16+
*.p12
17+
*.key
18+
*.mobileprovision
19+
20+
# Metro
21+
.metro-health-check*
22+
23+
# debug
24+
npm-debug.*
25+
yarn-debug.*
26+
yarn-error.*
27+
28+
# macOS
29+
.DS_Store
30+
*.pem
31+
32+
# local env files
33+
.env*.local
34+
35+
# typescript
36+
*.tsbuildinfo
37+
38+
ios
39+
android
40+
41+
!yarn.lock
42+
43+
# detox
44+
artifacts
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { LAUNCHDARKLY_MOBILE_KEY } from '@env';
2+
3+
import {
4+
AutoEnvAttributes,
5+
LDProvider,
6+
ReactNativeLDClient,
7+
} from '@launchdarkly/react-native-client-sdk';
8+
9+
import Welcome from './src/welcome';
10+
11+
const featureClient = new ReactNativeLDClient(LAUNCHDARKLY_MOBILE_KEY, AutoEnvAttributes.Enabled, {
12+
debug: true,
13+
applicationInfo: {
14+
id: 'ld-rn-fdv2-test-app',
15+
version: '0.0.1',
16+
},
17+
// @ts-ignore dataSystem is @internal
18+
dataSystem: {},
19+
});
20+
21+
const App = () => (
22+
<LDProvider client={featureClient}>
23+
<Welcome />
24+
</LDProvider>
25+
);
26+
27+
export default App;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# LaunchDarkly React Native SDK - FDv2 Example App
2+
3+
This is a minimal example app that demonstrates the experimental FDv2 data system
4+
for the React Native SDK.
5+
6+
> **Note:** FDv2 support is `@internal` and experimental. It is not ready for
7+
> production use and may change or be removed without notice.
8+
9+
## Features Demonstrated
10+
11+
- SDK initialization with the `dataSystem` option (FDv2 protocol)
12+
- Connection mode switching for all FDv2 modes:
13+
- **Streaming** - real-time flag updates with polling fallback
14+
- **Polling** - periodic polling only
15+
- **Offline** - cached flags only, no network
16+
- **One-Shot** - initialize then stop (no persistent synchronizer)
17+
- **Background** - low-frequency polling for background state
18+
- **Automatic** - clear the override and use automatic mode selection
19+
- Context identification
20+
- Boolean flag evaluation
21+
22+
## Quickstart
23+
24+
1. At the js-core repo root, install dependencies and build:
25+
26+
```shell
27+
yarn && yarn build
28+
```
29+
30+
2. Create an `.env` file in this directory (`example-fdv2/`) with your mobile key:
31+
32+
```shell
33+
LAUNCHDARKLY_MOBILE_KEY=mob-your-mobile-key-here
34+
```
35+
36+
3. Update the flag key in `src/welcome.tsx` if needed (defaults to `sample-feature`).
37+
38+
4. Run the app:
39+
40+
```shell
41+
# iOS
42+
yarn ios
43+
44+
# Android
45+
yarn android
46+
```
47+
48+
> **Note:** You may need to run `npx expo prebuild` before the first iOS or
49+
> Android build.
50+
51+
## Caveats
52+
53+
- **Network-based automatic mode switching** is not yet implemented. The wiring
54+
is in place, but `RNStateDetector` does not yet emit network state changes.
55+
Lifecycle-based switching (foreground/background) works.
56+
- The `dataSystem` option and `setConnectionMode()` are marked `@internal` and
57+
require `@ts-ignore` to use from TypeScript.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"expo": {
3+
"name": "react-native-example-fdv2",
4+
"slug": "react-native-example-fdv2",
5+
"version": "1.0.0",
6+
"orientation": "portrait",
7+
"userInterfaceStyle": "light",
8+
"assetBundlePatterns": ["**/*"],
9+
"ios": {
10+
"supportsTablet": true,
11+
"bundleIdentifier": "com.anonymous.reactnativeexamplefdv2"
12+
},
13+
"android": {
14+
"adaptiveIcon": {
15+
"backgroundColor": "#ffffff"
16+
},
17+
"package": "com.anonymous.reactnativeexamplefdv2"
18+
}
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = function (api) {
2+
api.cache(true);
3+
return {
4+
presets: ['babel-preset-expo'],
5+
plugins: [
6+
[
7+
'module:react-native-dotenv',
8+
{
9+
safe: true,
10+
allowUndefined: false,
11+
},
12+
],
13+
],
14+
};
15+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// We have to use a custom entrypoint for monorepo workspaces to work.
2+
// https://docs.expo.dev/guides/monorepos/#change-default-entrypoint
3+
import { registerRootComponent } from 'expo';
4+
5+
import App from './App';
6+
7+
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
8+
// It also ensures that whether you load the app in Expo Go or in a native build,
9+
// the environment is set up appropriately
10+
registerRootComponent(App);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// We need to use a custom metro config for monorepo workspaces to work.
2+
// https://docs.expo.dev/guides/monorepos/#modify-the-metro-config
3+
/**
4+
* @type {import('expo/metro-config')}
5+
*/
6+
const { getDefaultConfig } = require('expo/metro-config');
7+
const path = require('path');
8+
9+
// Find the project and workspace directories
10+
const projectRoot = __dirname;
11+
// This can be replaced with `find-yarn-workspace-root`
12+
const workspaceRoot = path.resolve(projectRoot, '../../../..');
13+
14+
const config = getDefaultConfig(projectRoot);
15+
16+
// 1. Watch all files within the monorepo
17+
config.watchFolders = [workspaceRoot];
18+
// 2. Let Metro know where to resolve packages and in what order
19+
config.resolver.nodeModulesPaths = [
20+
path.resolve(projectRoot, 'node_modules'),
21+
path.resolve(workspaceRoot, 'node_modules'),
22+
];
23+
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
24+
config.resolver.disableHierarchicalLookup = true;
25+
26+
module.exports = config;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@launchdarkly/react-native-example-fdv2",
3+
"private": true,
4+
"version": "0.0.1",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "expo start --reset-cache",
8+
"android": "expo run:android",
9+
"ios": "expo run:ios",
10+
"web": "expo start --web --clear"
11+
},
12+
"dependencies": {
13+
"@launchdarkly/react-native-client-sdk": "10.16.0",
14+
"@react-native-async-storage/async-storage": "^2.0.0",
15+
"expo": "52.0.14",
16+
"expo-status-bar": "~1.11.1",
17+
"react": "18.3.1",
18+
"react-native": "0.76.3",
19+
"react-native-dotenv": "^3.4.9"
20+
},
21+
"devDependencies": {
22+
"@babel/core": "^7.20.0",
23+
"@types/react": "~18.2.55",
24+
"@types/react-native-dotenv": "^0.2.1",
25+
"typescript": "^5.2.2"
26+
},
27+
"packageManager": "yarn@3.4.1",
28+
"installConfig": {
29+
"hoistingLimits": "workspaces"
30+
}
31+
}

0 commit comments

Comments
 (0)