Skip to content

Commit 1e92222

Browse files
authored
feat(feedback): manual widget display (#5236)
1 parent 77e097d commit 1e92222

File tree

16 files changed

+232
-38
lines changed

16 files changed

+232
-38
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- Apps can now manually show and hide the included feedback widget button (#5236)
8+
39
## 8.50.2
410

511
### Fixes

Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ public enum SentrySDKOverrides {
5858

5959
public var boolValue: Bool {
6060
get {
61-
switch self {
62-
case .disableAutoInject: return getBoolOverride(for: rawValue)
63-
default: return getBoolOverride(for: rawValue)
64-
}
61+
return getBoolOverride(for: rawValue)
6562
}
6663
set(newValue) {
6764
setBoolOverride(for: rawValue, value: newValue)

Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ extension UserFeedbackUITests {
489489
XCTAssertEqual(try dictionaryFromSuccessHookFile(), ["name": testName, "message": "UITest user feedback", "email": testContactEmail])
490490
}
491491

492+
// MARK: Alternative widget control
493+
492494
func testFormShowsAndDismissesProperlyWithCustomButton() {
493495
launchApp(args: ["--io.sentry.feedback.use-custom-feedback-button"])
494496

@@ -512,6 +514,16 @@ extension UserFeedbackUITests {
512514
XCTAssert(customButton.isHittable)
513515
XCTAssertFalse(widgetButton.isHittable)
514516
}
517+
518+
func testManuallyDisplayingWidget() {
519+
launchApp(args: ["--io.sentry.feedback.no-auto-inject-widget"])
520+
XCTAssertFalse(widgetButton.isHittable)
521+
extrasAreaTabBarButton.tap()
522+
app.buttons["io.sentry.ui-test.button.show-widget"].tap()
523+
XCTAssert(widgetButton.isHittable)
524+
app.buttons["io.sentry.ui-test.button.hide-widget"].tap()
525+
XCTAssertFalse(widgetButton.isHittable)
526+
}
515527
}
516528

517529
// MARK: UI Element access

Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard

Lines changed: 59 additions & 31 deletions
Large diffs are not rendered by default.

Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,4 +348,20 @@ class ExtraViewController: UIViewController {
348348
result["item_header_type"] = json["type"]
349349
}
350350
}
351+
352+
@IBAction func showFeedbackWidget(_ sender: Any) {
353+
if #available(iOS 13.0, *) {
354+
SentrySDK.feedback.showWidget()
355+
} else {
356+
showToast(in: self, type: .warning, message: "Feedback widget only available in iOS 13 or later.")
357+
}
358+
}
359+
360+
@IBAction func hideFeedbackWidget(_ sender: Any) {
361+
if #available(iOS 13.0, *) {
362+
SentrySDK.feedback.hideWidget()
363+
} else {
364+
showToast(in: self, type: .warning, message: "Feedback widget only available in iOS 13 or later.")
365+
}
366+
}
351367
}

Sentry.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,8 @@
697697
845C16D52A622A5B00EC9519 /* SentryTracer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 845C16D42A622A5B00EC9519 /* SentryTracer+Private.h */; };
698698
845CEAEF2D83F79500B6B325 /* SentryProfilingPublicAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845CEAEE2D83F79500B6B325 /* SentryProfilingPublicAPITests.swift */; };
699699
845CEB172D8A979700B6B325 /* SentryAppStartProfilingConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845CEB162D8A979700B6B325 /* SentryAppStartProfilingConfigurationTests.swift */; };
700+
8482FA9B2DD7C397000E9283 /* SentryFeedbackAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 8482FA992DD7C397000E9283 /* SentryFeedbackAPI.h */; settings = {ATTRIBUTES = (Public, ); }; };
701+
8482FA9C2DD7C397000E9283 /* SentryFeedbackAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 8482FA9A2DD7C397000E9283 /* SentryFeedbackAPI.m */; };
700702
848A45192BBF8D33006AAAEC /* SentryContinuousProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 848A45182BBF8D33006AAAEC /* SentryContinuousProfiler.mm */; };
701703
848A451A2BBF8D33006AAAEC /* SentryContinuousProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 848A45172BBF8D33006AAAEC /* SentryContinuousProfiler.h */; };
702704
848A451D2BBF9504006AAAEC /* SentryProfilerTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 848A451C2BBF9504006AAAEC /* SentryProfilerTestHelpers.m */; };
@@ -1864,6 +1866,8 @@
18641866
845CEAEE2D83F79500B6B325 /* SentryProfilingPublicAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryProfilingPublicAPITests.swift; sourceTree = "<group>"; };
18651867
845CEB162D8A979700B6B325 /* SentryAppStartProfilingConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAppStartProfilingConfigurationTests.swift; sourceTree = "<group>"; };
18661868
846F90332D56F59D009E86C1 /* Brewfile-ci-deploy */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = "Brewfile-ci-deploy"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
1869+
8482FA992DD7C397000E9283 /* SentryFeedbackAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryFeedbackAPI.h; sourceTree = "<group>"; };
1870+
8482FA9A2DD7C397000E9283 /* SentryFeedbackAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryFeedbackAPI.m; sourceTree = "<group>"; };
18671871
848A45172BBF8D33006AAAEC /* SentryContinuousProfiler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryContinuousProfiler.h; path = ../include/SentryContinuousProfiler.h; sourceTree = "<group>"; };
18681872
848A45182BBF8D33006AAAEC /* SentryContinuousProfiler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryContinuousProfiler.mm; sourceTree = "<group>"; };
18691873
848A451B2BBF9504006AAAEC /* SentryProfilerTestHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryProfilerTestHelpers.h; path = ../include/SentryProfilerTestHelpers.h; sourceTree = "<group>"; };
@@ -3770,6 +3774,8 @@
37703774
children = (
37713775
849B8F9E2C70091A00148E1F /* Configuration */,
37723776
849B8F962C6E906900148E1F /* SentryUserFeedbackIntegrationDriver.swift */,
3777+
8482FA992DD7C397000E9283 /* SentryFeedbackAPI.h */,
3778+
8482FA9A2DD7C397000E9283 /* SentryFeedbackAPI.m */,
37733779
84DBC62B2CE82F0E000C4904 /* SentryFeedback.swift */,
37743780
84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h */,
37753781
84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.m */,
@@ -4348,6 +4354,7 @@
43484354
0A9BF4E428A114B50068D266 /* SentryViewHierarchyIntegration.h in Headers */,
43494355
D8BBD32728FD9FC00011F850 /* SentrySwift.h in Headers */,
43504356
84CFA4CE2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h in Headers */,
4357+
8482FA9B2DD7C397000E9283 /* SentryFeedbackAPI.h in Headers */,
43514358
8E4E7C7425DAAB49006AB9E2 /* SentrySpanProtocol.h in Headers */,
43524359
8EC4CF4A25C38DAA0093DEE9 /* SentrySpanStatus.h in Headers */,
43534360
8ECC673D25C23996000E2BF6 /* SentrySpanId.h in Headers */,
@@ -5260,6 +5267,7 @@
52605267
63FE710B20DA4C1000CDBAE8 /* SentryCrashMach.c in Sources */,
52615268
63FE707720DA4C1000CDBAE8 /* SentryDictionaryDeepSearch.m in Sources */,
52625269
621D9F2F2B9B0320003D94DE /* SentryCurrentDateProvider.swift in Sources */,
5270+
8482FA9C2DD7C397000E9283 /* SentryFeedbackAPI.m in Sources */,
52635271
7BC9A20628F41781001E7C4C /* SentryMeasurementUnit.m in Sources */,
52645272
63FE71A020DA4C1100CDBAE8 /* SentryCrashInstallation.m in Sources */,
52655273
63FE713520DA4C1100CDBAE8 /* SentryCrashMemory.c in Sources */,

Sources/Sentry/Public/Sentry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ FOUNDATION_EXPORT const unsigned char SentryVersionString[];
2020
# import <Sentry/SentryError.h>
2121
# import <Sentry/SentryEvent.h>
2222
# import <Sentry/SentryException.h>
23+
# import <Sentry/SentryFeedbackAPI.h>
2324
# import <Sentry/SentryFrame.h>
2425
# import <Sentry/SentryGeo.h>
2526
# import <Sentry/SentryHttpStatusCodeRange.h>

Sources/Sentry/Public/SentryOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ typedef void (^SentryProfilingConfigurationBlock)(SentryProfileOptions *_Nonnull
798798
* either this block to configure a widget and UI form to gather feedback, or directly submits
799799
* feedback you've gathered using your own UI by calling the method @c SentrySDK.captureFeedback
800800
* (se https://docs.sentry.io/platforms/apple/user-feedback/configuration/).
801+
* @note User feedback widget is only available for iOS 13 or later.
801802
*/
802803
@property (nonatomic, copy, nullable)
803804
SentryUserFeedbackConfigurationBlock configureUserFeedback API_AVAILABLE(ios(13.0));

Sources/Sentry/Public/SentrySDK.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@class SentryBreadcrumb;
1010
@class SentryEvent;
1111
@class SentryFeedback;
12+
@class SentryFeedbackAPI;
1213
@class SentryId;
1314
@class SentryMetricsAPI;
1415
@class SentryOptions;
@@ -263,13 +264,20 @@ SENTRY_NO_INIT
263264

264265
/**
265266
* Captures user feedback that was manually gathered and sends it to Sentry.
267+
* @warning This is an experimental feature and may still have bugs.
266268
* @param feedback The feedback to send to Sentry.
267269
* @note If you'd prefer not to have to build the UI required to gather the feedback from the user,
268270
* see @c SentryOptions.configureUserFeedback to customize a fully managed integration. See
269271
* https://docs.sentry.io/platforms/apple/user-feedback/ for more information.
270272
*/
271273
+ (void)captureFeedback:(SentryFeedback *)feedback NS_SWIFT_NAME(capture(feedback:));
272274

275+
#if TARGET_OS_IOS && SENTRY_HAS_UIKIT
276+
277+
@property (nonatomic, class, readonly) SentryFeedbackAPI *feedback API_AVAILABLE(ios(13.0));
278+
279+
#endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT
280+
273281
/**
274282
* Adds a Breadcrumb to the current Scope of the current Hub. If the total number of breadcrumbs
275283
* exceeds the @c SentryOptions.maxBreadcrumbs the SDK removes the oldest breadcrumb.

Sources/Sentry/SentrySDK.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#import "SentrySerialization.h"
2727
#import "SentrySwift.h"
2828
#import "SentryTransactionContext.h"
29+
#import "SentryUserFeedbackIntegration.h"
2930

3031
#if TARGET_OS_OSX
3132
# import "SentryCrashExceptionApplication.h"
@@ -34,6 +35,9 @@
3435
#if SENTRY_HAS_UIKIT
3536
# import "SentryUIDeviceWrapper.h"
3637
# import "SentryUIViewControllerPerformanceTracker.h"
38+
# if TARGET_OS_IOS
39+
# import "SentryFeedbackAPI.h"
40+
# endif // TARGET_OS_IOS
3741
#endif // SENTRY_HAS_UIKIT
3842

3943
#if SENTRY_TARGET_PROFILING_SUPPORTED
@@ -431,6 +435,18 @@ + (void)captureFeedback:(SentryFeedback *)feedback
431435
[SentrySDK.currentHub captureFeedback:feedback];
432436
}
433437

438+
#if TARGET_OS_IOS && SENTRY_HAS_UIKIT
439+
440+
+ (SentryFeedbackAPI *)feedback
441+
{
442+
static SentryFeedbackAPI *feedbackAPI;
443+
static dispatch_once_t onceToken;
444+
dispatch_once(&onceToken, ^{ feedbackAPI = [[SentryFeedbackAPI alloc] init]; });
445+
return feedbackAPI;
446+
}
447+
448+
#endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT
449+
434450
+ (void)addBreadcrumb:(SentryBreadcrumb *)crumb
435451
{
436452
[SentrySDK.currentHub addBreadcrumb:crumb];

0 commit comments

Comments
 (0)