feat(feedback): implement shake gesture detection#7579
Conversation
The useShakeGesture configuration property existed but was not implemented. This adds SentryShakeDetector which swizzles UIWindow.motionEnded:withEvent: to detect shake gestures and wires it into SentryUserFeedbackIntegrationDriver. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛
Internal Changes 🔧Deps
Samples
Other
🤖 This preview updates automatically when you update the PR. |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7579 +/- ##
=============================================
- Coverage 86.173% 85.327% -0.846%
=============================================
Files 483 484 +1
Lines 28785 28837 +52
Branches 12504 12532 +28
=============================================
- Hits 24805 24606 -199
- Misses 3927 4184 +257
+ Partials 53 47 -6
... and 12 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
…it targets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…non-iOS The @interface declaration was wrapped in #if TARGET_OS_IOS, causing a compile error on macOS/tvOS/watchOS where the @implementation in the #else block could not find the interface. The class is now declared on all platforms with the methods documented as no-ops on non-iOS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use monotonic clock (CACurrentMediaTime) instead of NSDate, atomic_bool for thread-safe enabled flag, bridged notification constant, and unconditional cleanup in deinit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Show resolved
Hide resolved
Track whether this driver instance enabled shake detection and only call disable in deinit if it did. Prevents an old driver's deallocation from disabling shake detection that a new driver already re-enabled during SDK restart.
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Show resolved
Hide resolved
The init early-returns when there are no connected scenes (SwiftUI apps). Move observeScreenshots and observeShakeGesture calls before the return so shake detection works in SwiftUI apps.
Add a "Shake to Report" section to the user feedback setup page and update the `useShakeGesture` config description to note it's iOS-only. Ref: getsentry/sentry-cocoa#7579 Co-Authored-By: Claude <noreply@anthropic.com>
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Outdated
Show resolved
Hide resolved
Replaces the ObjC implementation with a Swift class while maintaining full ObjC compatibility via @objc annotations. The class name remains SentryShakeDetector in ObjC, preserving compatibility with sentry-react-native.
The class is a static utility not meant to be subclassed. Regenerate API snapshot accordingly.
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Outdated
Show resolved
Hide resolved
Always call SentryShakeDetector.disable() in deinit instead of tracking whether this instance enabled it.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Shake in early-return path permanently blocks future shakes
- showForm(screenshot:) now early-returns when no presenter exists, preventing displayingForm from being set true when presentation cannot occur.
Or push these changes by commenting:
@cursor push 3846cd567d
Preview (3846cd567d)
diff --git a/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift b/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
--- a/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
+++ b/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
@@ -120,11 +120,14 @@
@available(iOSApplicationExtension, unavailable)
private extension SentryUserFeedbackIntegrationDriver {
func showForm(screenshot: UIImage?) {
+ guard let presenter else {
+ return
+ }
let form = SentryUserFeedbackFormController(config: configuration, delegate: self, screenshot: screenshot)
form.presentationController?.delegate = self
widget?.rootVC.setWidget(visible: false, animated: configuration.animations)
displayingForm = true
- presenter?.present(form, animated: configuration.animations) {
+ presenter.present(form, animated: configuration.animations) {
self.configuration.onFormOpen?()
}
}
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Show resolved
Hide resolved
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Show resolved
Hide resolved
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Outdated
Show resolved
Hide resolved
Add SDK debug logs to swizzling steps in SentryShakeDetector and to shake gesture handling in the integration driver.
Guard showForm early if presenter is nil so displayingForm does not permanently block future shake and screenshot triggers.
Sources/Swift/Integrations/UserFeedback/SentryShakeDetector.swift
Outdated
Show resolved
Hide resolved
…IWindow Co-authored-by: Philip Niedertscheider <phil.niedertscheider@sentry.io>
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Outdated
Show resolved
Hide resolved
Sentry Build Distribution
|
The one-shot screenshot observer is wasted in the early-return path where no presenter exists yet. Keep only the persistent shake observer there.
Sentry Build Distribution
|
Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackIntegrationDriver.swift
Show resolved
Hide resolved
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 93d7fdf | 1225.77 ms | 1259.79 ms | 34.02 ms |
| 3bff9ff | 1217.72 ms | 1246.43 ms | 28.71 ms |
| 62d9cf0 | 1228.72 ms | 1258.29 ms | 29.57 ms |
| c796e6d | 1226.33 ms | 1247.33 ms | 21.00 ms |
| c58a1c5 | 1229.49 ms | 1251.19 ms | 21.70 ms |
| 26ae2f8 | 1221.61 ms | 1250.76 ms | 29.14 ms |
| d064999 | 1218.36 ms | 1250.08 ms | 31.72 ms |
| 3f47e70 | 1210.67 ms | 1233.44 ms | 22.76 ms |
| d427374 | 1226.81 ms | 1257.74 ms | 30.94 ms |
| 3b01aaf | 1194.98 ms | 1210.36 ms | 15.38 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 93d7fdf | 24.14 KiB | 1.11 MiB | 1.08 MiB |
| 3bff9ff | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| 62d9cf0 | 24.14 KiB | 1.09 MiB | 1.07 MiB |
| c796e6d | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| c58a1c5 | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| 26ae2f8 | 24.14 KiB | 1.11 MiB | 1.09 MiB |
| d064999 | 24.14 KiB | 1.12 MiB | 1.09 MiB |
| 3f47e70 | 24.14 KiB | 1.12 MiB | 1.09 MiB |
| d427374 | 24.14 KiB | 1.09 MiB | 1.07 MiB |
| 3b01aaf | 24.14 KiB | 1.06 MiB | 1.04 MiB |
Sentry Build Distribution
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
…16791) ## DESCRIBE YOUR PR Document the now-functional `useShakeGesture` option for Apple/Cocoa user feedback, based on [sentry-cocoa#7579](getsentry/sentry-cocoa#7579). - Add "Shake to Report" section to the user feedback setup page with a Swift example - Update `useShakeGesture` config description to note it's iOS-only (no-op on macOS, tvOS, watchOS, visionOS) ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. - [ ] Urgent deadline (GA date, etc.): - [ ] Other deadline: - [x] None: Not urgent, can wait up to 1 week+⚠️ Should be merged after getsentry/sentry-cocoa#7579 is released ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've added an urgent due date to it. Thanks in advance for your help! ## PRE-MERGE CHECKLIST *Make sure you've checked the following before merging your changes:* - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>

📜 Description
Implements shake gesture detection for the user feedback form. When
useShakeGestureis enabled inSentryUserFeedbackConfiguration, shaking the device opens the feedback form.The implementation swizzles
UIWindow.motionEnded:withEvent:usingclass_addMethod+method_setImplementationto safely intercept shake events without modifyingUIResponder's inherited implementation. A cooldown of 1 second prevents duplicate triggers.Note: this is exposed to be used on React Native too getsentry/sentry-react-native#5754
💡 Motivation and Context
The
useShakeGestureproperty already existed inSentryUserFeedbackConfigurationbut was never wired up. This PR implements the feature. The implementation is placed here (sentry-cocoa) rather than in each SDK separately so it can be reused by all SDKs that embed the feedback UI (React Native, Flutter, .NET MAUI, Unity).💚 How did you test it?
SentryShakeDetector📝 Checklist
sendDefaultPIIis enabled.Closes #7597