Skip to content

Conversation

@philprime
Copy link
Member

@philprime philprime commented Nov 4, 2025

📜 Description

This PR addresses issue #3901 by automatically disabling App Hang (ANR) tracking for Widgets, Live Activities, (Siri) Intent Extensions, Action Extensions and Share Extensions. These components run in separate processes or sandboxes with different execution characteristics, which can cause false positive app hang reports.

  • Added SentryExtensionDetector to detect the current extension context by reading Info.plist
  • Added SentryExtensionType enum for extension point identifiers (WidgetKit, Intents, Action, Share extensions)
  • Modified SentryANRTrackingIntegration to check for extensions before installing ANR tracking
  • Extended SentryInfoPlistWrapper to support reading dictionary values from Info.plist

💚 How did you test it?

Testing:

  • Added comprehensive unit tests for SentryExtensionDetector covering all extension types and error cases
  • Added integration tests for SentryANRTrackingIntegration verifying correct behavior in extension contexts
  • Added tests for SentryExtensionType enum

Sample Applications:

To properly test the edge cases I created additional sample apps and extensions. Some of them required newer iOS versions than iOS 15 which is still used by iOS-Swift.

Testing - General

I implemented samples with UI to visualize internal state using available UI, e.g. using a Siri Message Preview or a widget. As the App Hang tracker is disabled by default when a debugger is attached, you have two options to verify behaviour:

  • Perform the test without a debugger attached
  • Comment out the following lines of code, so that the App Hang Tracker is also installed with a debugger attached:

if ((integrationOptions & kIntegrationOptionDebuggerNotAttached) &&
[SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
[self logWithReason:@"because the debugger is attached"];
return NO;
}

The easiest way to verify that the changes in this PR actually change anything is removing/commenting disabled extension types in the SentryExtensionDetector:

private static var disabledAppHangTypes: [SentryExtensionType] {
return [
.widget,
.intent,
.action,
.share
]
}

Testing - Share Extension

Before:

  1. Remove the .share from the disabled app hang types in the SentryExtensionDetector
  2. Run the Xcode scheme iOS-Swift on an iOS 18.6 simulator or iOS device
  3. Stop the app
  4. Open Safari on the device and navigate to any page, e.g. https://docs.sentry.io
  5. Tap the share symbol in the toolbar
  6. Tap on iOS-Swift in the list of applications
  7. The share dialog will appear with Sentry Enabled? ✅ but ANR Disabled? ❌, indicating that we are in the falsey state and still have ANR tracking enabled.
share-before.mov

After:

  1. Ensure the .share is in the list of disabled app hang types in the SentryExtensionDetector
  2. Perform steps 2 to 6 similar explained above
  3. The share dialog will appear with Sentry Enabled? ✅ but ANR Disabled? ✅ , indicating that we are in the truthy state and have ANR tracking disabled.
share-after.mov

Testing - Action Extension

  1. Remove the .action from the disabled app hang types in the SentryExtensionDetector
  2. Run the Xcode scheme iOS-Swift on an iOS 18.6 simulator or iOS device
  3. Stop the app
  4. Open Safari on the device and navigate to any page, e.g. https://docs.sentry.io
  5. Tap the share symbol in the toolbar
  6. Scroll to the bottom and tap on the action iOS-Swift-ActionExtension
  7. The share dialog will appear with ✅ Sentry is enabled but ❌ ANR Tracking installed, indicating that we are in the falsey state and still have ANR tracking enabled.
action-before.mov

After:

  1. Ensure the .action is in the list of disabled app hang types in the SentryExtensionDetector
  2. Perform steps 2 to 6 similar explained above
  3. The action view will appear with ✅ Sentry is enabled and ✅ ANR Tracking not installed , indicating that we are in the truthy state and have ANR tracking disabled.
action-after.mov

Testing - Intent Extension

Important

I was unsuccessful in using the extension in the simulator, but it worked on device. Therefore I recommend using a real device, but it might also work in the simulator

Before:

  1. Remove the .intent from the disabled app hang types in the SentryExtensionDetector
  2. Run the Xcode scheme iOS-Swift on an iOS device
  3. Stop the app
  4. Trigger Siri and ask it to "Send a message via iOS-Swift".
  5. The sample will pick a fake recipient "John Snow" and show a preview message with Sentry Enabled? ✅ but ANR Disabled? ❌
  6. Tap on "Send" to view the sent message UI
  7. The sample will show a sent message UI with Sentry Enabled? ✅ but ANR Disabled? ❌ too

NOTE: iOS sometimes seems to cache the preview but the message after "sending it" should be correct.

intent-before.MP4

After:

  1. Ensure the .intent is in the list of disabled app hang types in the SentryExtensionDetector
  2. Perform steps 2 to 4 as explained above
    ...
  3. The sample will pick a fake recipient "John Snow" and show a preview message with Sentry Enabled? ✅ but ANR Disabled? ✅
  4. Tap on "Send" to view the sent message UI
  5. The sample will show a sent message UI with Sentry Enabled? ✅ but ANR Disabled? ✅ too
intent-after.MP4

Testing - Widget

For this test we are disabling debugger detection and keep the debugger attached, because it causes a widget refresh on every run.

Before:

  1. Remove the .widget from the disabled app hang types in the SentryExtensionDetector
  2. Disable the debugger detection by commenting out the following code:

if ((integrationOptions & kIntegrationOptionDebuggerNotAttached) &&
[SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
[self logWithReason:@"because the debugger is attached"];
return NO;
}

  1. Run the Xcode scheme iOS-SwiftUI-Widgets-WidgetsExtension on an iOS simulator
  2. It will install the app and create a widget on the home screen.
  3. The widget will display SDK Enabled? ✅ but ANR Disabled? ❌ with the current timestamp (which is important because widgets can be outdated)
widget-before.mov

After:

  1. Ensure the .widget is in the list of disabled app hang types in the SentryExtensionDetector
  2. Perform steps 2 to 4 from the before test
  3. The widget will display SDK Enabled? ✅ and ANR Disabled? ✅ with the current timestamp (which is important because widgets can be outdated)
widget-after.mov

Testing - Widget Control

Before:

  1. Remove the .widget from the disabled app hang types in the SentryExtensionDetector
  2. Disable the debugger detection by commenting out the following code:

if ((integrationOptions & kIntegrationOptionDebuggerNotAttached) &&
[SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
[self logWithReason:@"because the debugger is attached"];
return NO;
}

  1. Run the Xcode scheme iOS-SwiftUI-Widgets-WidgetControl on an iOS simulator
  2. It will install the app and create a control in the widget control center.
  3. If the symbol of the control is a X, it indicates ANR tracking is enabled.
widget-control-before.mov

After:

  1. Ensure the .widget is in the list of disabled app hang types in the SentryExtensionDetector
  2. Disable the debugger detection by commenting out the following code:

if ((integrationOptions & kIntegrationOptionDebuggerNotAttached) &&
[SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
[self logWithReason:@"because the debugger is attached"];
return NO;
}

  1. Run the Xcode scheme iOS-SwiftUI-Widgets-WidgetControl on an iOS simulator
  2. It will install the app and create a control in the widget control center.
  3. If the symbol of the control is a ✔️, it indicates ANR tracking is enabled.
widget-control-after.mov

Testing - Live Activities

Live Activities are displayed differently on different devices, so I recommend you use an iPhone 16 Pro with an dynamic island to see it there too.

IMPORTANT:

During testing I experience multiple times that Live Activities were not started properly, with the push notification token never showing up. Further investigation showed that the live activity is actually a separate process which is called WidgetRenderer_Activities in combination with chronod. During compile-time some of the extensions seemed to perform only partial rebuilds, causing theses processes to not being able to unarchive the live activities.

Furthermore, live activities can be considered special kinds of widgets, therefore they are in the same kind of app extension.

Before:

  1. Remove the .widget from the disabled app hang types in the SentryExtensionDetector
  2. Disable the debugger detection by commenting out the following code:

if ((integrationOptions & kIntegrationOptionDebuggerNotAttached) &&
[SentryDependencyContainer.sharedInstance.crashWrapper isBeingTraced]) {
[self logWithReason:@"because the debugger is attached"];
return NO;
}

  1. Run the Xcode scheme iOS-SwiftUI-Widgets on an iOS simulator
  2. Tap on the button "Start Live Activity"
  3. It will start an indefinite local live activity and display the push notification token locally with "✅ Live Activity is running"
  4. Lock the screen (CMD + L)
  5. In the lockscreen it should display a live activity with "SDK Enabled? ✅" and "ANR Disabled? ❌) indicating that the ANR tracker is still installed
  6. Unlock the simulator and go to the home screen (CMD + SHIFT + H, repeat 3x)
  7. It will display a ✅ left to the dynamic island indicating the SDK is enabled, but ❌ on the right side indicating that the ANR tracker is still installed
live-activity-before.mov

After:

  1. Ensure the .widget is in the list of disabled app hang types in the SentryExtensionDetector
  2. Perform steps 2 to 6 from the before test
  3. In the lockscreen it should display a live activity with "SDK Enabled? ✅" and "ANR Disabled? ✅ ) indicating that the ANR tracker is not installed anymore
  4. Unlock the simulator and go to the home screen (CMD + SHIFT + H, repeat 3x)
  5. It will display a ✅ left to the dynamic island indicating the SDK is enabled, but ✅ on the right side indicating that the ANR tracker is not installed
live-activity-after.mov

@philprime philprime self-assigned this Nov 4, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 4, 2025

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

Generated by 🚫 dangerJS against c289fbf

@codecov
Copy link

codecov bot commented Nov 4, 2025

Codecov Report

❌ Patch coverage is 86.84211% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.022%. Comparing base (26c5e2c) to head (c289fbf).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
Sources/Swift/Helper/SentryExtensionDetector.swift 74.285% 8 Missing and 1 partial ⚠️
Sources/Sentry/SentrySDKInternal.m 0.000% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##              main     #6670       +/-   ##
=============================================
+ Coverage   85.006%   85.022%   +0.015%     
=============================================
  Files          452       454        +2     
  Lines        27686     27762       +76     
  Branches     12157     12201       +44     
=============================================
+ Hits         23535     23604       +69     
- Misses        4105      4114        +9     
+ Partials        46        44        -2     
Files with missing lines Coverage Δ
SentryTestUtils/Sources/TestInfoPlistWrapper.swift 100.000% <100.000%> (ø)
Sources/Sentry/SentryANRTrackingIntegration.m 96.212% <100.000%> (+0.212%) ⬆️
Sources/Swift/Helper/Dependencies.swift 100.000% <100.000%> (ø)
...wift/Helper/InfoPlist/SentryInfoPlistWrapper.swift 96.666% <100.000%> (+0.512%) ⬆️
Sources/Swift/Helper/SentryExtensionType.swift 100.000% <100.000%> (ø)
Sources/Sentry/SentrySDKInternal.m 85.140% <0.000%> (-0.344%) ⬇️
Sources/Swift/Helper/SentryExtensionDetector.swift 74.285% <74.285%> (ø)

... and 8 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 26c5e2c...c289fbf. Read the comment docs.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 4, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1222.88 ms 1246.00 ms 23.13 ms
Size 24.14 KiB 1.02 MiB 1016.63 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
b44bf23 1214.60 ms 1251.75 ms 37.15 ms
b115f82 1212.96 ms 1251.92 ms 38.96 ms
664c060 1215.48 ms 1244.41 ms 28.93 ms
939d583 1209.96 ms 1251.09 ms 41.13 ms
f83dcc4 1210.88 ms 1246.22 ms 35.35 ms
f246093 1219.75 ms 1247.35 ms 27.60 ms
7a8b167 1217.49 ms 1247.67 ms 30.18 ms
ea3f7b6 1228.14 ms 1258.71 ms 30.57 ms
f8cad3c 1217.94 ms 1257.96 ms 40.02 ms
9c75c11 1228.42 ms 1262.81 ms 34.39 ms

App size

Revision Plain With Sentry Diff
b44bf23 23.75 KiB 1022.78 KiB 999.03 KiB
b115f82 23.75 KiB 989.04 KiB 965.30 KiB
664c060 23.74 KiB 977.41 KiB 953.67 KiB
939d583 23.75 KiB 1023.82 KiB 1000.07 KiB
f83dcc4 23.75 KiB 1.02 MiB 1019.11 KiB
f246093 23.75 KiB 1022.79 KiB 999.04 KiB
7a8b167 24.14 KiB 1.01 MiB 1014.00 KiB
ea3f7b6 23.75 KiB 1.01 MiB 1008.77 KiB
f8cad3c 23.75 KiB 1.01 MiB 1016.13 KiB
9c75c11 23.74 KiB 1022.95 KiB 999.20 KiB

Previous results on branch: philprime/disable-live-activity-widgets

Startup times

Revision Plain With Sentry Diff
10db8d4 1225.04 ms 1254.57 ms 29.53 ms
1171923 1232.80 ms 1251.69 ms 18.90 ms
48849e2 1218.48 ms 1246.26 ms 27.78 ms

App size

Revision Plain With Sentry Diff
10db8d4 23.75 KiB 1.00 MiB 1003.65 KiB
1171923 23.75 KiB 1.00 MiB 1001.57 KiB
48849e2 23.75 KiB 1.00 MiB 1001.61 KiB

@philprime philprime force-pushed the philprime/disable-live-activity-widgets branch from 095f7a8 to f8693fb Compare November 6, 2025 14:11
@philprime philprime changed the title feat(anr): Disable ANR tracking for widgets, live activities, and extensions feat(anr): Disable ANR tracking for widgets, live activities, action, intent and share extensions Nov 7, 2025
@philprime philprime marked this pull request as ready for review November 10, 2025 08:17
Co-authored-by: phil.niedertscheider <phil.niedertscheider@sentry.io>
Co-authored-by: phil.niedertscheider <phil.niedertscheider@sentry.io>
Copy link
Contributor

@itaybre itaybre left a comment

Choose a reason for hiding this comment

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

LGTM, left some comments for discussion

@philprime
Copy link
Member Author

While working on this PR I noticed that the SentryANRTrackingIntegrationTests are kind of patching a state of initialized dependencies, e.g. install only works when SentryFileManager is set in the SentryDependencyContainer.sharedInstance(), which can cause further issues in other tests due to global state. While we do call clearTestState() at the end, discarding the shared container, I believe this is bad practice and should be cleaned up.
To keep the scope of the PR where it is, we will do this in a follow-up PR.

@philprime philprime enabled auto-merge (squash) November 13, 2025 12:10
@philprime philprime added the ready-to-merge Use this label to trigger all PR workflows label Nov 13, 2025
…r, IntentHandler, and ShareViewController
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants