Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add `SentrySDK.feedback.enableShakeGesture()` and `SentrySDK.feedback.disableShakeGesture()` for runtime control of shake-to-report ([#7737](https://github.com/getsentry/sentry-cocoa/pull/7737))

## 9.8.0

### Features
Expand Down
16 changes: 16 additions & 0 deletions Sources/Swift/Integrations/UserFeedback/SentryFeedbackAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@
@objc public func hideWidget() {
getIntegration()?.driver.hideWidget()
}

/// Enable shake gesture to show the feedback form.
/// Call this to dynamically enable shake-to-report at runtime.
/// - warning: This is an experimental feature and may still have bugs.
@available(iOSApplicationExtension, unavailable)
@objc public func enableShakeGesture() {
getIntegration()?.driver.enableShakeGesture()
}

/// Disable shake gesture for the feedback form.
/// Call this to dynamically disable shake-to-report at runtime.
/// - warning: This is an experimental feature and may still have bugs.
@available(iOSApplicationExtension, unavailable)
@objc public func disableShakeGesture() {
getIntegration()?.driver.disableShakeGesture()
}

private func getIntegration() -> UserFeedbackIntegration<SentryDependencyContainer>? {
SentrySDKInternal.currentHub().getInstalledIntegration(UserFeedbackIntegration<SentryDependencyContainer>.self) as? UserFeedbackIntegration<SentryDependencyContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ public final class SentryShakeDetector: NSObject {
enabled = false
SentrySDKLog.debug("Shake detector: disabled")
}

#if SENTRY_TEST || SENTRY_TEST_CI
/// Resets cooldown state for testing. Not available in production builds.
static func resetCooldownForTesting() {
lastShakeTimestamp = 0
}
#endif
#else
/// No-op on non-iOS platforms.
@objc public static func enable() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ final class SentryUserFeedbackIntegrationDriver: NSObject {
widget?.rootVC.setWidget(visible: false, animated: configuration.animations)
}

func enableShakeGesture() {
// Remove any existing observer first to prevent duplicate notifications
NotificationCenter.default.removeObserver(self, name: .SentryShakeDetected, object: nil)
SentryShakeDetector.enable()
NotificationCenter.default.addObserver(self, selector: #selector(handleShakeGesture), name: .SentryShakeDetected, object: nil)
}

func disableShakeGesture() {
SentryShakeDetector.disable()
NotificationCenter.default.removeObserver(self, name: .SentryShakeDetected, object: nil)
}

@objc func showForm(sender: UIButton) {
presenter?.present(SentryUserFeedbackFormController(config: configuration, delegate: self, screenshot: nil), animated: configuration.animations) {
self.configuration.onFormOpen?()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ final class SentryShakeDetectorTests: XCTestCase {
override func tearDown() {
super.tearDown()
SentryShakeDetector.disable()
SentryShakeDetector.resetCooldownForTesting()
}

func testEnable_whenShakeOccurs_shouldPostNotification() {
Expand Down Expand Up @@ -82,6 +83,19 @@ final class SentryShakeDetectorTests: XCTestCase {
NotificationCenter.default.removeObserver(observer)
}

func testReEnable_afterDisable_shouldPostNotification() {
SentryShakeDetector.enable()
SentryShakeDetector.disable()
SentryShakeDetector.enable()

let expectation = expectation(forNotification: .SentryShakeDetected, object: nil)

let window = UIWindow()
window.motionEnded(.motionShake, with: nil)

wait(for: [expectation], timeout: 1.0)
}

func testOriginalImplementation_shouldStillBeCalled() {
SentryShakeDetector.enable()

Expand Down
44 changes: 44 additions & 0 deletions sdk_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -37230,6 +37230,50 @@
"printedName": "init()",
"usr": "c:@M@Sentry@objc(cs)SentryFeedbackAPI(im)init"
},
{
"children": [
{
"kind": "TypeNominal",
"name": "Void",
"printedName": "()"
}
],
"declAttributes": [
"Available",
"Final",
"ObjC"
],
"declKind": "Func",
"funcSelfKind": "NonMutating",
"kind": "Function",
"mangledName": "$s6Sentry0A11FeedbackAPIC19disableShakeGestureyyF",
"moduleName": "Sentry",
"name": "disableShakeGesture",
"printedName": "disableShakeGesture()",
"usr": "c:@M@Sentry@objc(cs)SentryFeedbackAPI(im)disableShakeGesture"
},
{
"children": [
{
"kind": "TypeNominal",
"name": "Void",
"printedName": "()"
}
],
"declAttributes": [
"Available",
"Final",
"ObjC"
],
"declKind": "Func",
"funcSelfKind": "NonMutating",
"kind": "Function",
"mangledName": "$s6Sentry0A11FeedbackAPIC18enableShakeGestureyyF",
"moduleName": "Sentry",
"name": "enableShakeGesture",
"printedName": "enableShakeGesture()",
"usr": "c:@M@Sentry@objc(cs)SentryFeedbackAPI(im)enableShakeGesture"
},
{
"children": [
{
Expand Down
Loading