feat(core): Forward feature flag evaluations to the native SDKs - #6613
Conversation
`featureFlagsIntegration` is now owned by the React Native SDK. On top of the JavaScript buffering it forwards every boolean flag evaluation to the native SDKs via `RNSentry.addFeatureFlag`, so flags are attached to native crashes and native error events too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
🤖 This preview updates automatically when you update the PR. |
|
|
@cursor review |
|
@sentry review |
|
|
||
| export { featureFlagsIntegration } | ||
| // @public | ||
| export const featureFlagsIntegration: () => FeatureFlagsIntegration; |
There was a problem hiding this comment.
No consumer-facing API change here: featureFlagsIntegration keeps the same name, call signature, and FeatureFlagsIntegration return type. The diff only reflects that it's now an RN-owned implementation (adds native forwarding) instead of a straight re-export from @sentry/browser. Non-breaking.
📲 Install BuildsAndroid
|
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| f9c1ed4+dirty | 431.00 ms | 466.22 ms | 35.22 ms |
| c2e182c+dirty | 471.64 ms | 553.59 ms | 81.95 ms |
| 41d6254+dirty | 424.45 ms | 474.34 ms | 49.89 ms |
| af33f3b+dirty | 434.90 ms | 506.14 ms | 71.24 ms |
| 882f8ae+dirty | 399.98 ms | 427.06 ms | 27.08 ms |
| 0b5120f+dirty | 503.22 ms | 538.60 ms | 35.38 ms |
| 774257e+dirty | 407.09 ms | 447.46 ms | 40.37 ms |
| bf168a4+dirty | 418.21 ms | 489.74 ms | 71.53 ms |
| 5569641+dirty | 406.43 ms | 428.51 ms | 22.08 ms |
| c151573+dirty | 530.34 ms | 559.43 ms | 29.09 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| f9c1ed4+dirty | 49.74 MiB | 54.86 MiB | 5.12 MiB |
| c2e182c+dirty | 49.74 MiB | 54.85 MiB | 5.11 MiB |
| 41d6254+dirty | 48.30 MiB | 53.60 MiB | 5.30 MiB |
| af33f3b+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 882f8ae+dirty | 48.30 MiB | 53.60 MiB | 5.29 MiB |
| 0b5120f+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| 774257e+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| bf168a4+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 5569641+dirty | 48.30 MiB | 53.48 MiB | 5.18 MiB |
| c151573+dirty | 48.30 MiB | 53.54 MiB | 5.24 MiB |
Previous results on branch: feat/native-feature-flags
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bcebd82+dirty | 424.21 ms | 449.40 ms | 25.19 ms |
| fa9430a+dirty | 425.22 ms | 451.79 ms | 26.57 ms |
| e92d59c+dirty | 433.43 ms | 460.31 ms | 26.89 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bcebd82+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
| fa9430a+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
| e92d59c+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
… host app `NATIVE.addFeatureFlag` throws when the native module isn't loaded (e.g. an OTA JS update without a native rebuild). Wrap the forward in try/catch and log via `debug.error`, matching the expoContext pattern, so a native failure never propagates into the host app's flag-evaluation path. The flag is already buffered on the JavaScript scope beforehand. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a1a5a34. Configure here.
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ecf47a2+dirty | 3835.20 ms | 1222.43 ms | -2612.77 ms |
| 3817909+dirty | 1183.90 ms | 1187.50 ms | 3.60 ms |
| 5ee78d6+dirty | 3847.52 ms | 1224.27 ms | -2623.25 ms |
| 890d145+dirty | 1223.59 ms | 1231.37 ms | 7.78 ms |
| e763471+dirty | 3841.16 ms | 1218.06 ms | -2623.09 ms |
| 3d31fcf+dirty | 3838.09 ms | 1223.46 ms | -2614.63 ms |
| 9210ae6+dirty | 3815.93 ms | 1214.14 ms | -2601.79 ms |
| 26843eb+dirty | 3861.60 ms | 1238.77 ms | -2622.82 ms |
| 7a89652+dirty | 3861.46 ms | 1229.61 ms | -2631.85 ms |
| 9ad4522+dirty | 3850.40 ms | 1222.40 ms | -2628.00 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| ecf47a2+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 3817909+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 5ee78d6+dirty | 5.15 MiB | 6.69 MiB | 1.53 MiB |
| 890d145+dirty | 3.38 MiB | 4.77 MiB | 1.38 MiB |
| e763471+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 3d31fcf+dirty | 4.98 MiB | 6.56 MiB | 1.58 MiB |
| 9210ae6+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 26843eb+dirty | 4.98 MiB | 6.53 MiB | 1.55 MiB |
| 7a89652+dirty | 5.15 MiB | 6.70 MiB | 1.55 MiB |
| 9ad4522+dirty | 4.98 MiB | 6.56 MiB | 1.58 MiB |
Previous results on branch: feat/native-feature-flags
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| fa9430a+dirty | 3846.89 ms | 1220.89 ms | -2625.99 ms |
| e92d59c+dirty | 3860.32 ms | 1227.87 ms | -2632.44 ms |
| bcebd82+dirty | 3837.53 ms | 1216.83 ms | -2620.71 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| fa9430a+dirty | 5.08 MiB | 6.75 MiB | 1.67 MiB |
| e92d59c+dirty | 5.08 MiB | 6.72 MiB | 1.64 MiB |
| bcebd82+dirty | 5.08 MiB | 6.75 MiB | 1.67 MiB |
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 1217.70 ms | 1224.69 ms | 6.99 ms |
| 0b1b5e3+dirty | 3820.72 ms | 1207.94 ms | -2612.78 ms |
| 44abcc2+dirty | 3841.42 ms | 1214.77 ms | -2626.65 ms |
| 1d3572b+dirty | 3830.71 ms | 1222.23 ms | -2608.49 ms |
| 6177334+dirty | 3851.52 ms | 1226.23 ms | -2625.29 ms |
| 5ee78d6+dirty | 3815.71 ms | 1217.04 ms | -2598.67 ms |
| 083e3ef+dirty | 3871.96 ms | 1234.95 ms | -2637.01 ms |
| ce7b368+dirty | 3851.41 ms | 1222.37 ms | -2629.04 ms |
| 7887847+dirty | 3844.89 ms | 1221.67 ms | -2623.22 ms |
| 1122a96+dirty | 3839.17 ms | 1219.23 ms | -2619.93 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 0b1b5e3+dirty | 5.15 MiB | 6.70 MiB | 1.54 MiB |
| 44abcc2+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| 1d3572b+dirty | 4.98 MiB | 6.56 MiB | 1.58 MiB |
| 6177334+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 5ee78d6+dirty | 5.15 MiB | 6.69 MiB | 1.53 MiB |
| 083e3ef+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| ce7b368+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 7887847+dirty | 4.98 MiB | 6.46 MiB | 1.48 MiB |
| 1122a96+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
Previous results on branch: feat/native-feature-flags
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| fa9430a+dirty | 3853.21 ms | 1224.00 ms | -2629.21 ms |
| e92d59c+dirty | 3828.51 ms | 1198.98 ms | -2629.53 ms |
| bcebd82+dirty | 3876.00 ms | 1239.43 ms | -2636.57 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| fa9430a+dirty | 5.08 MiB | 6.75 MiB | 1.67 MiB |
| e92d59c+dirty | 5.08 MiB | 6.72 MiB | 1.64 MiB |
| bcebd82+dirty | 5.08 MiB | 6.75 MiB | 1.67 MiB |
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5a010b7+dirty | 475.15 ms | 519.02 ms | 43.88 ms |
| f9c1ed4+dirty | 520.57 ms | 594.84 ms | 74.27 ms |
| c2e182c+dirty | 468.50 ms | 545.44 ms | 76.94 ms |
| 2c735cc+dirty | 435.20 ms | 459.48 ms | 24.28 ms |
| af33f3b+dirty | 417.68 ms | 448.04 ms | 30.37 ms |
| 7d8c8bd+dirty | 406.06 ms | 460.88 ms | 54.81 ms |
| 890d145+dirty | 486.42 ms | 514.85 ms | 28.43 ms |
| ef27341+dirty | 519.02 ms | 553.42 ms | 34.40 ms |
| bf168a4+dirty | 430.60 ms | 459.31 ms | 28.71 ms |
| 41d6254+dirty | 406.20 ms | 445.52 ms | 39.32 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5a010b7+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| f9c1ed4+dirty | 49.74 MiB | 54.86 MiB | 5.12 MiB |
| c2e182c+dirty | 49.74 MiB | 54.85 MiB | 5.11 MiB |
| 2c735cc+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| af33f3b+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 7d8c8bd+dirty | 48.30 MiB | 53.54 MiB | 5.23 MiB |
| 890d145+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
| ef27341+dirty | 48.30 MiB | 53.54 MiB | 5.24 MiB |
| bf168a4+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 41d6254+dirty | 48.30 MiB | 53.60 MiB | 5.30 MiB |
Previous results on branch: feat/native-feature-flags
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bcebd82+dirty | 609.13 ms | 648.67 ms | 39.53 ms |
| fa9430a+dirty | 488.72 ms | 526.73 ms | 38.01 ms |
| e92d59c+dirty | 564.89 ms | 622.49 ms | 57.60 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bcebd82+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
| fa9430a+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
| e92d59c+dirty | 49.74 MiB | 55.45 MiB | 5.71 MiB |
`addFeatureFlag` isn't a scope-sync method like the others in the helper (it's forwarded directly by the integration, not via scopeSync), and the call had no matching assertion. Its behavior, including the enableNative gate, is already covered by the dedicated addFeatureFlag test block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add new features and fixes for version 8.24.0.
📢 Type of change
📜 Description
featureFlagsIntegrationis now owned by the React Native SDK instead of being re-exported from@sentry/browser. It wraps the JavaScript integration (so JS buffering,processEvent, and span attribution are unchanged) and additionally forwards every boolean flag evaluation to the native SDKs.The native call path:
featureFlagsIntegration().addFeatureFlag(name, value)→NATIVE.addFeatureFlag→RNSentry.addFeatureFlag→ AndroidSentry.addFeatureFlag(String, boolean)/ iOSSentrySDK.addFeatureFlag(name:result:).On iOS the Cocoa API is
@nonobjc, so the call is routed through the existingRNSentryInternalSwift bridge (same pattern already used for replay/profiling). Non-boolean values are ignored on the native side, matching the JS flag buffer which already ignores them.The public API surface is unchanged for consumers —
Sentry.featureFlagsIntegration()keeps the same name, signature, and return type. It remains opt-in (not a default integration).💡 Motivation and Context
Before this change, feature flags were only buffered on the JavaScript scope and attached to events captured on the JS layer. Native crashes never ran the JS
processEvent, so they carried no flags. Both native SDKs now ship the feature flags API (sentry-android 8.53.0, sentry-cocoa 9.26.0), so we can forward evaluations to the native scope and attach flags to native crashes too.Fixes #5367
💚 How did you test it?
NATIVE.addFeatureFlag: boolean true/false,enableNative=false).turboModuleContextskip-list test.📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps