Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(user feedback): display a button in a window that presents a vc #4364

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ - (BOOL)application:(UIApplication *)application
containsObject:@"--io.sentry.iOS-Swift.auto-inject-user-feedback-widget"]) {
widget.labelText = @"Report Jank";
widget.widgetAccessibilityLabel = @"io.sentry.iOS-Swift.button.report-jank";
widget.layoutUIOffset = UIOffsetMake(12, 50);
} else {
widget.autoInject = NO;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@
argument = "--disable-file-io-tracing"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--io.sentry.iOS-Swift.user-feedback.icon-only-widget"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--io.sentry.iOS-Swift.user-feedback.all-defaults"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--skip-sentry-init"
isEnabled = "NO">
Expand Down
19 changes: 16 additions & 3 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
options.enableNetworkBreadcrumbs = !args.contains("--disable-network-breadcrumbs")
options.enableSwizzling = !args.contains("--disable-swizzling")
options.enableCrashHandler = !args.contains("--disable-crash-handler")
options.enableTracing = !args.contains("--disable-tracing")

Check warning on line 117 in Samples/iOS-Swift/iOS-Swift/AppDelegate.swift

View workflow job for this annotation

GitHub Actions / iOS-Swift UI Tests iPhone 15 (17.2)

'enableTracing' is deprecated: Use tracesSampleRate or tracesSampler instead

// because we run CPU for 15 seconds at full throttle, we trigger ANR issues being sent. disable such during benchmarks.
options.enableAppHangTracking = !isBenchmarking && !args.contains("--disable-anr-tracking")
Expand Down Expand Up @@ -165,24 +165,37 @@
}

options.configureUserFeedback = { config in
guard !args.contains("--io.sentry.iOS-Swift.user-feedback.all-defaults") else {
config.configureWidget = { widget in
widget.layoutUIOffset = .init(horizontal: 12, vertical: 50)
}
return
}
config.useShakeGesture = true
config.showFormForScreenshots = true
config.configureWidget = { widget in
if args.contains("--io.sentry.iOS-Swift.auto-inject-user-feedback-widget") {
widget.labelText = "Report Jank"
widget.widgetAccessibilityLabel = "io.sentry.iOS-Swift.button.report-jank"
widget.layoutUIOffset = .init(horizontal: 12, vertical: 50)
} else {
widget.autoInject = false
}
if args.contains("--io.sentry.iOS-Swift.user-feedback.icon-only-widget") {
widget.labelText = nil
}
}
config.configureForm = { uiForm in
uiForm.formTitle = "Jank Report"
uiForm.submitButtonLabel = "Report that jank"
uiForm.addScreenshotButtonLabel = "Show us the jank"
uiForm.messagePlaceholder = "Describe the nature of the jank. Its essence, if you will."
uiForm.themeOverrides = { theme in
theme.font = UIFont(name: "Comic Sans", size: 25)
}
}
config.configureTheme = { theme in
theme.font = UIFont(name: "ChalkboardSE-Regular", size: 60)
theme.outlineColor = .purple
theme.foreground = .purple
theme.background = .purple.withAlphaComponent(0.1)
}
config.onSubmitSuccess = { info in
let name = info["name"] ?? "$shakespearean_insult_name"
Expand Down
16 changes: 16 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@
84B7FA4429B2924000AD93B1 /* TestRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E25C97425F8511A00DC215B /* TestRandom.swift */; };
84B7FA4529B2926900AD93B1 /* TestDisplayLinkWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B30B68126527C55006B2752 /* TestDisplayLinkWrapper.swift */; };
84B7FA4629B2935F00AD93B1 /* ClearTestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD47B4C268F0B080076A663 /* ClearTestState.swift */; };
84BA62272CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BA62262CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift */; };
84CFA4CA2C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */; };
84CFA4CD2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.m in Sources */ = {isa = PBXBuildFile; fileRef = 84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.m */; };
84CFA4CE2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.h */; };
84DEE86B2B686BD400A7BC17 /* SentrySamplerDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DEE86A2B686BD400A7BC17 /* SentrySamplerDecision.h */; };
84DEE8762B69AD6400A7BC17 /* SentryLaunchProfiling.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DEE8752B69AD6400A7BC17 /* SentryLaunchProfiling.h */; };
84EB21942BF01C6C00EDDA28 /* TestNSNotificationCenterWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B18DE4328D9F8F6004845C6 /* TestNSNotificationCenterWrapper.swift */; };
Expand Down Expand Up @@ -1791,7 +1795,11 @@
84AF45A429A7FFA500FBB177 /* SentryProfiledTracerConcurrency.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryProfiledTracerConcurrency.h; path = ../include/SentryProfiledTracerConcurrency.h; sourceTree = "<group>"; };
84AF45A529A7FFA500FBB177 /* SentryProfiledTracerConcurrency.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfiledTracerConcurrency.mm; sourceTree = "<group>"; };
84B7FA3B29B2866200AD93B1 /* SentryTestUtils-ObjC-BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryTestUtils-ObjC-BridgingHeader.h"; sourceTree = "<group>"; };
84BA62262CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackWidgetButtonView.swift; sourceTree = "<group>"; };
84C47B2B2A09239100DAEB8A /* .codecov.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .codecov.yml; sourceTree = "<group>"; };
84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackWidget.swift; sourceTree = "<group>"; };
84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryUserFeedbackIntegrationShell.h; sourceTree = "<group>"; };
84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryUserFeedbackIntegrationShell.m; sourceTree = "<group>"; };
84DEE86A2B686BD400A7BC17 /* SentrySamplerDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentrySamplerDecision.h; path = include/SentrySamplerDecision.h; sourceTree = "<group>"; };
84DEE8752B69AD6400A7BC17 /* SentryLaunchProfiling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryLaunchProfiling.h; path = Sources/Sentry/include/SentryLaunchProfiling.h; sourceTree = SOURCE_ROOT; };
84EACEBC2C33CA7A009B8753 /* SentryWithoutUIKit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; name = SentryWithoutUIKit.modulemap; path = Sources/Resources/SentryWithoutUIKit.modulemap; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -3574,6 +3582,10 @@
children = (
849B8F9E2C70091A00148E1F /* Configuration */,
849B8F962C6E906900148E1F /* SentryUserFeedbackIntegration.swift */,
84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.h */,
84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.m */,
84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */,
84BA62262CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift */,
);
name = UserFeedback;
path = ../Swift/Integrations/UserFeedback;
Expand Down Expand Up @@ -4032,6 +4044,7 @@
8E133FA625E72EB400ABD0BF /* SentrySamplingContext.h in Headers */,
0A9BF4E428A114B50068D266 /* SentryViewHierarchyIntegration.h in Headers */,
D8BBD32728FD9FC00011F850 /* SentrySwift.h in Headers */,
84CFA4CE2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.h in Headers */,
8E4E7C7425DAAB49006AB9E2 /* SentrySpanProtocol.h in Headers */,
8EC4CF4A25C38DAA0093DEE9 /* SentrySpanStatus.h in Headers */,
8ECC673D25C23996000E2BF6 /* SentrySpanId.h in Headers */,
Expand Down Expand Up @@ -4614,6 +4627,7 @@
63FE712F20DA4C1100CDBAE8 /* SentryCrashSysCtl.c in Sources */,
7B3B473825D6CC7E00D01640 /* SentryNSError.m in Sources */,
621AE74D2C626C510012E730 /* SentryANRTrackerV2.m in Sources */,
84CFA4CA2C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift in Sources */,
D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */,
7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */,
51B15F7E2BE88A7C0026A2F2 /* URLSessionTaskHelper.swift in Sources */,
Expand Down Expand Up @@ -4649,6 +4663,7 @@
7B63459F280EBA7200CFA05A /* SentryUIEventTracker.m in Sources */,
7BF9EF782722B35D00B5BBEF /* SentrySubClassFinder.m in Sources */,
D80CD8D32B751447002F710B /* SentryMXCallStackTree.swift in Sources */,
84CFA4CD2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegrationShell.m in Sources */,
7BCFA71627D0BB50008C662C /* SentryANRTrackerV1.m in Sources */,
8459FCC02BD73EB20038E9C9 /* SentryProfilerSerialization.mm in Sources */,
63EED6C02237923600E02400 /* SentryOptions.m in Sources */,
Expand Down Expand Up @@ -4786,6 +4801,7 @@
635B3F391EBC6E2500A6176D /* SentryAsynchronousOperation.m in Sources */,
63FE717520DA4C1100CDBAE8 /* SentryCrash.m in Sources */,
6344DDB11EC308E400D9160D /* SentryCrashInstallationReporter.m in Sources */,
84BA62272CAE2EEF0049F636 /* SentryUserFeedbackWidgetButtonView.swift in Sources */,
D85596F3280580F10041FF8B /* SentryScreenshotIntegration.m in Sources */,
7BAF3DCE243DCBFE008A5414 /* SentryTransportFactory.m in Sources */,
844EDC70294143B900C86F34 /* SentryNSProcessInfoWrapper.mm in Sources */,
Expand Down
56 changes: 33 additions & 23 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,33 @@
BOOL _enableTracingManual;
}

+ (NSArray<NSString *> *)defaultIntegrations
NSMutableArray<NSString *> *
sentry_defaultIntegrations(void)
{
// The order of integrations here is important.
// SentryCrashIntegration needs to be initialized before SentryAutoSessionTrackingIntegration.
// And SentrySessionReplayIntegration before SentryCrashIntegration.
NSMutableArray<NSString *> *defaultIntegrations =
@[
NSMutableArray<NSString *> *defaultIntegrations = [NSMutableArray<NSString *> arrayWithObjects:
#if SENTRY_HAS_UIKIT && !TARGET_OS_VISION
NSStringFromClass([SentrySessionReplayIntegration class]),
#endif
NSStringFromClass([SentryCrashIntegration class]),
NSStringFromClass([SentryCrashIntegration class]),
#if SENTRY_HAS_UIKIT
NSStringFromClass([SentryAppStartTrackingIntegration class]),
NSStringFromClass([SentryFramesTrackingIntegration class]),
NSStringFromClass([SentryPerformanceTrackingIntegration class]),
NSStringFromClass([SentryScreenshotIntegration class]),
NSStringFromClass([SentryUIEventTrackingIntegration class]),
NSStringFromClass([SentryViewHierarchyIntegration class]),
NSStringFromClass([SentryWatchdogTerminationTrackingIntegration class]),
NSStringFromClass([SentryAppStartTrackingIntegration class]),
NSStringFromClass([SentryFramesTrackingIntegration class]),
NSStringFromClass([SentryPerformanceTrackingIntegration class]),
NSStringFromClass([SentryScreenshotIntegration class]),
NSStringFromClass([SentryUIEventTrackingIntegration class]),
NSStringFromClass([SentryViewHierarchyIntegration class]),
NSStringFromClass([SentryWatchdogTerminationTrackingIntegration class]),
#endif // SENTRY_HAS_UIKIT
NSStringFromClass([SentryANRTrackingIntegration class]),
NSStringFromClass([SentryAutoBreadcrumbTrackingIntegration class]),
NSStringFromClass([SentryAutoSessionTrackingIntegration class]),
NSStringFromClass([SentryCoreDataTrackingIntegration class]),
NSStringFromClass([SentryFileIOTrackingIntegration class]),
NSStringFromClass([SentryNetworkTrackingIntegration class]),
NSStringFromClass([SentrySwiftAsyncIntegration class])
]
.mutableCopy;
NSStringFromClass([SentryANRTrackingIntegration class]),
NSStringFromClass([SentryAutoBreadcrumbTrackingIntegration class]),
NSStringFromClass([SentryAutoSessionTrackingIntegration class]),
NSStringFromClass([SentryCoreDataTrackingIntegration class]),
NSStringFromClass([SentryFileIOTrackingIntegration class]),
NSStringFromClass([SentryNetworkTrackingIntegration class]),
NSStringFromClass([SentrySwiftAsyncIntegration class]), nil];

#if SENTRY_HAS_METRIC_KIT
if (@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, *)) {
Expand All @@ -78,6 +76,11 @@
return defaultIntegrations;
}

+ (NSArray<NSString *> *)defaultIntegrations
{
return sentry_defaultIntegrations();
}

- (instancetype)init
{
if (self = [super init]) {
Expand All @@ -91,7 +94,7 @@
self.debug = NO;
self.maxBreadcrumbs = defaultMaxBreadcrumbs;
self.maxCacheItems = 30;
_integrations = SentryOptions.defaultIntegrations;
_integrations = sentry_defaultIntegrations();
self.sampleRate = SENTRY_DEFAULT_SAMPLE_RATE;
self.enableAutoSessionTracking = YES;
self.enableGraphQLOperationTracking = NO;
Expand Down Expand Up @@ -250,7 +253,7 @@
SENTRY_LOG_WARN(
@"Setting `SentryOptions.integrations` is deprecated. Integrations should be enabled or "
@"disabled using their respective `SentryOptions.enable*` property.");
_integrations = integrations;
_integrations = integrations.mutableCopy;
}

- (void)setDsn:(NSString *)dsn
Expand Down Expand Up @@ -363,7 +366,8 @@
}

if ([options[@"integrations"] isKindOfClass:[NSArray class]]) {
self.integrations = [options[@"integrations"] filteredArrayUsingPredicate:isNSString];
self.integrations =
[[options[@"integrations"] filteredArrayUsingPredicate:isNSString] mutableCopy];
}

if ([options[@"sampleRate"] isKindOfClass:[NSNumber class]]) {
Expand Down Expand Up @@ -792,6 +796,12 @@
#endif // defined(RELEASE)
}

- (void)setConfigureUserFeedback:(SentryUserFeedbackConfigurationBlock)configureUserFeedback
{
self.userFeedbackConfiguration = [[SentryUserFeedbackConfiguration alloc] init];
configureUserFeedback(self.userFeedbackConfiguration);

Check warning on line 802 in Sources/Sentry/SentryOptions.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentryOptions.m#L801-L802

Added lines #L801 - L802 were not covered by tests
}

#if defined(DEBUG) || defined(TEST) || defined(TESTCI)
- (NSString *)debugDescription
{
Expand Down
8 changes: 7 additions & 1 deletion Sources/Sentry/SentrySDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "SentrySerialization.h"
#import "SentrySwift.h"
#import "SentryTransactionContext.h"
#import "SentryUserFeedbackIntegrationShell.h"

#if TARGET_OS_OSX
# import "SentryCrashExceptionApplication.h"
Expand Down Expand Up @@ -459,7 +460,12 @@
return;
}
SentryOptions *options = [SentrySDK.currentHub getClient].options;
for (NSString *integrationName in [SentrySDK.currentHub getClient].options.integrations) {
NSMutableArray<NSString *> *integrationNames =
[SentrySDK.currentHub getClient].options.integrations.mutableCopy;
if (options.userFeedbackConfiguration != nil) {
[integrationNames addObject:NSStringFromClass([SentryUserFeedbackIntegrationShell class])];

Check warning on line 466 in Sources/Sentry/SentrySDK.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentrySDK.m#L466

Added line #L466 was not covered by tests
}
for (NSString *integrationName in integrationNames) {
Class integrationClass = NSClassFromString(integrationName);
if (nil == integrationClass) {
SENTRY_LOG_ERROR(@"[SentryHub doInstallIntegrations] "
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryOptions+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ FOUNDATION_EXPORT NSString *const kSentryDefaultEnvironment;
*/
@property (nonatomic, copy, nullable) SentryUserFeedbackConfigurationBlock configureUserFeedback;

@property (nonatomic, strong, nullable) SentryUserFeedbackConfiguration *userFeedbackConfiguration;

SENTRY_EXTERN BOOL sentry_isValidSampleRate(NSNumber *sampleRate);

@property (nonatomic, assign) BOOL enableAppHangTrackingV2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import UIKit
*/
public var configureWidget: ((SentryUserFeedbackWidgetConfiguration) -> Void)?

lazy var widgetConfig = SentryUserFeedbackWidgetConfiguration()

/**
* Use a shake gesture to display the form.
* - note: Default: `false`
Expand All @@ -33,6 +35,8 @@ import UIKit
* - note: Default: `nil`
*/
public var configureForm: ((SentryUserFeedbackFormConfiguration) -> Void)?

lazy var formConfig = SentryUserFeedbackFormConfiguration()

/**
* Tags to set on the feedback event. This is a dictionary where keys are strings
Expand Down Expand Up @@ -76,6 +80,29 @@ import UIKit
* that function.
*/
public var onSubmitError: ((Error) -> Void)?

// MARK: Theme

/**
* Builder for default/light theme overrides.
* - note: On iOS versions predating dark mode (≤12) this is the only theme override used. Apps
* running on later versions that include dark mode should also consider `configureDarkTheme`.
* - note: Default: `nil`
*/
public var configureTheme: ((SentryUserFeedbackThemeConfiguration) -> Void)?

lazy var theme = SentryUserFeedbackThemeConfiguration()

/**
* Builder for dark mode theme overrides. If your app does not deploy a different theme for dark
* mode, but you still want to override some theme settings, assign the same builder to this
* property as you do for `configureTheme`.
* - note: Default: `nil`
* - note: Only applies to iOS ≤12.
*/
public var configureDarkTheme: ((SentryUserFeedbackThemeConfiguration) -> Void)?

lazy var darkTheme = SentryUserFeedbackThemeConfiguration()
}

#endif // (os(iOS) || os(tvOS)) && !SENTRY_NO_UIKIT
Original file line number Diff line number Diff line change
Expand Up @@ -160,23 +160,6 @@ import UIKit
* - note: Default: `confirmButtonLabel` value
*/
public var confirmButtonAccessibilityLabel: String?

// MARK: Theme

/**
* Builder for default/light theme overrides.
* - note: On iOS versions predating dark mode (≤12) this is the only theme override used. Apps
* running on later versions that include dark mode should also consider `darkThemeOverrides`.
* - note: Default: `nil`
*/
public var themeOverrides: ((SentryUserFeedbackThemeConfiguration) -> Void)?

/**
* Builder for dark mode theme overrides. If your app does not deploy a different theme for dark
* mode, assign the same builder to this property as you do for `themeOverrides`.
* - note: Default: `nil`
*/
public var darkThemeOverrides: ((SentryUserFeedbackThemeConfiguration) -> Void)?
}

#endif // (os(iOS) || os(tvOS)) && !SENTRY_NO_UIKIT
Loading
Loading