Skip to content

Conversation

@antonis
Copy link
Contributor

@antonis antonis commented Mar 7, 2025

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

Based on feat: Capture app start errors before JS

📜 Description

Adds RNSentrySDK APIs support to @sentry/react-native/expo plugin by importing sentry and adding injecting RNSentrySDK.init/start in the Android MainApplication (Kotlin or Java) or AppDelegate (Objective-C or Swift).

This feature is opt-out to enable it set useNativeInit to true in your @sentry/react-native/expo plugin configuration.

"plugins": [
  [
    "@sentry/react-native/expo",
    {
      "useNativeInit": true
    }
  ],

💡 Motivation and Context

Fixes #4625

💚 How did you test it?

CI, Manual

📝 Checklist

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 3885d70

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2025

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1209.39 ms 1214.90 ms 5.51 ms
Size 2.63 MiB 3.78 MiB 1.15 MiB

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2025

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1234.24 ms 1237.90 ms 3.65 ms
Size 3.19 MiB 4.35 MiB 1.17 MiB

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2025

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 441.12 ms 451.55 ms 10.43 ms
Size 17.75 MiB 20.15 MiB 2.40 MiB

@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2025

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 351.04 ms 404.94 ms 53.90 ms
Size 7.15 MiB 8.42 MiB 1.27 MiB

@antonis antonis linked an issue Mar 7, 2025 that may be closed by this pull request
@antonis antonis marked this pull request as ready for review March 7, 2025 16:22
Copy link
Collaborator

@lucas-zimerman lucas-zimerman left a comment

Choose a reason for hiding this comment

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

Overall the PR looks good and thank you for the tests!
I still think we could fine tune a bit more the android part and after it we could get ready for merge

antonis and others added 2 commits April 4, 2025 11:01
Co-authored-by: LucasZF <lucas-zimerman1@hotmail.com>
@antonis antonis requested a review from lucas-zimerman April 4, 2025 09:45
@antonis
Copy link
Contributor Author

antonis commented Apr 4, 2025

Note: The Build & Test / Type Check Typescript 3.8 (pull_request) failure should be fixed when we merge #4673 from main

@antonis antonis requested a review from krystofwoldrich April 15, 2025 10:00
Co-authored-by: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com>
@antonis antonis requested a review from krystofwoldrich April 16, 2025 08:34
Copy link
Collaborator

@lucas-zimerman lucas-zimerman left a comment

Choose a reason for hiding this comment

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

I have re-checked the PR and overall, I only found a nit on a string. After fixing it and if @krystofwoldrich doesn't find anything else, LGTM!

Co-authored-by: LucasZF <lucas-zimerman1@hotmail.com>
Co-authored-by: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com>
@krystofwoldrich
Copy link
Contributor

I'm currently updating the base branch with the latest main since there was quite a lot of development that was not synced to the feature branch.

@antonis
Copy link
Contributor Author

antonis commented Jun 11, 2025

I'm currently updating the base branch with the latest main since there was quite a lot of development that was not synced to the feature branch.

Thank you for the heads up @krystofwoldrich 🙇
I'll update this branch after that along with the review feedback.

@krystofwoldrich krystofwoldrich force-pushed the capture-app-start-errors branch 3 times, most recently from 2363742 to 588ba6d Compare June 12, 2025 15:26
Copy link
Contributor

@krystofwoldrich krystofwoldrich left a comment

Choose a reason for hiding this comment

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

Thank you for the fixes. 🚀 It looks great!


### Features

- Add RNSentrySDK APIs support to @sentry/react-native/expo plugin ([#4633](https://github.com/getsentry/sentry-react-native/pull/4633))
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit. It would be nice to include an example code snippet and a small summary of what will the flag do.

### Features

- Add RNSentrySDK APIs support to @sentry/react-native/expo plugin ([#4633](https://github.com/getsentry/sentry-react-native/pull/4633))
Copy link
Contributor

Choose a reason for hiding this comment

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

  • 🚫 The changelog entry seems to be part of an already released section ## 6.15.1.
    Consider moving the entry to the ## Unreleased section, please.

@lucas-zimerman
Copy link
Collaborator

@sentry review

Comment on lines +78 to +81
config.modResults.contents = config.modResults.contents.replace(
/(super\.onCreate\(\)[;\n]*)([ \t]*)/,
`$1\n$2RNSentrySDK.init(this);\n$2`,
);

Choose a reason for hiding this comment

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

The regex pattern /(super\.onCreate\(\)[;\n]*)([ \t]*)/ may not handle all edge cases properly. For example, if there are multiple spaces or tabs before the next line, or if there are comments immediately after super.onCreate(), the insertion might not be placed correctly. Consider adding a test case for files with unusual formatting.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +107 to +109
/(func application\([^)]*\) -> Bool \{)\s*\n(\s*)/s,
`$1\n$2RNSentrySDK.start()\n$2`,
);

Choose a reason for hiding this comment

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

The regex pattern /(func application\([^)]*\) -> Bool \{)\s*\n(\s*)/s may fail to match if the function signature is split across multiple lines or has different whitespace patterns. Consider making the pattern more robust to handle multi-line function signatures:

/(func application\([^)]*\)\s*->\s*Bool\s*\{)\s*\n(\s*)/s

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +123 to +125
config.modResults.contents = config.modResults.contents.replace(
/(- \(BOOL\)application:[\s\S]*?didFinishLaunchingWithOptions:[\s\S]*?\{\n)(\s*)/s,
`$1$2[RNSentrySDK start];\n$2`,

Choose a reason for hiding this comment

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

The regex pattern /(- \(BOOL\)application:[\s\S]*?didFinishLaunchingWithOptions:[\s\S]*?\{\n)(\s*)/s is overly greedy and could potentially match unintended content if there are multiple methods with similar signatures. Consider making it more specific by including the method return or limiting the character classes:

/(- \(BOOL\)application:\(UIApplication \*\)\w+\s+didFinishLaunchingWithOptions:\(NSDictionary \*\)\w+\s*\{\n)(\s*)/s

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +174 to +181

it('should insert import statements only once in an Swift project', async () => {
config.modResults.contents =
'import UIKit\nimport RNSentrySDK\n\noverride func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {';

const result = (await modifyAppDelegate(config)) as MockedExpoConfig;

const importCount = (result.modResults.contents.match(/import RNSentrySDK/g) || []).length;

Choose a reason for hiding this comment

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

The test name and logic are inconsistent. The test checks for 'import RNSentrySDK' but the actual implementation imports 'import RNSentry'. This test should verify the correct import statement.

The expected import should be import RNSentry not import RNSentrySDK.

Suggested change
it('should insert import statements only once in an Swift project', async () => {
config.modResults.contents =
'import UIKit\nimport RNSentrySDK\n\noverride func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {';
const result = (await modifyAppDelegate(config)) as MockedExpoConfig;
const importCount = (result.modResults.contents.match(/import RNSentrySDK/g) || []).length;
it('should insert import statements only once in an Swift project', async () => {
config.modResults.contents =
'import UIKit\nimport RNSentry\n\noverride func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {';
const result = (await modifyAppDelegate(config)) as MockedExpoConfig;
const importCount = (result.modResults.contents.match(/import RNSentry/g) || []).length;
expect(importCount).toBe(1);
});

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +82 to +93
if (config.modResults.contents === originalContents) {
warnOnce(`Failed to insert 'RNSentrySDK.init' in '${fileName}'.`);
} else if (!config.modResults.contents.includes('import io.sentry.react.RNSentrySDK;')) {
// Insert import statement after package declaration
config.modResults.contents = config.modResults.contents.replace(
/(package .*;\n\n?)/,
`$1import io.sentry.react.RNSentrySDK;\n`,
);
}
} else if (config.modResults.language === 'kt') {
// Add RNSentrySDK.init
const originalContents = config.modResults.contents;

Choose a reason for hiding this comment

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

When the regex replacement fails and the contents remain unchanged, only a warning is logged but the import statement might still be added in the else block. This could result in an orphaned import statement without the corresponding initialization call. Consider checking if the replacement was successful before adding the import statement.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +109 to +115
);
if (config.modResults.contents === originalContents) {
warnOnce(`Failed to insert 'RNSentrySDK.start()' in '${fileName}'.`);
} else if (!config.modResults.contents.includes('import RNSentry')) {
// Insert import statement after UIKit import
config.modResults.contents = config.modResults.contents.replace(/(import UIKit\n)/, `$1import RNSentry\n`);
}

Choose a reason for hiding this comment

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

When the regex replacement fails and the contents remain unchanged, only a warning is logged but the import statement might still be added in the else block. This could result in an orphaned import statement without the corresponding initialization call. Consider checking if the replacement was successful before adding the import statement.

Did we get this right? 👍 / 👎 to inform future reviews.

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.

Add RNSentrySDK APIs support to @sentry/react-native/expo plugin

4 participants