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(feedback): capture envelopes #4535

Merged
merged 29 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
97ab954
prefill user info
armcknight Dec 12, 2024
8a9fe49
fix build and add ui test
armcknight Dec 12, 2024
fc35119
dont assert nil value bc it uses the placeholder string when empty
armcknight Dec 13, 2024
a2764d8
default user.name in addition to user.username
armcknight Dec 13, 2024
237f9b4
these should not have been in the public SentrySDK header
armcknight Dec 13, 2024
71f473a
capture envelopes
armcknight Nov 15, 2024
9220f58
just send from the form, no need for delegation
armcknight Nov 15, 2024
f66ad3b
put back integration objc
armcknight Nov 15, 2024
bc430c5
add new SentryFeedback interface and thread callpath to capture in en…
armcknight Nov 16, 2024
b78655c
fix headerdoc in SentrySDK.h; use new SDK capture method in form subm…
armcknight Nov 16, 2024
ec031c4
Format code
getsentry-bot Nov 16, 2024
0ac677a
pr feedback and updates
armcknight Nov 19, 2024
de56160
remove hints; ordering in init
armcknight Nov 20, 2024
c128d60
fix bad conflict resolution
armcknight Dec 5, 2024
9dc92aa
refactor method to reuse for grabbing attachments for an event
armcknight Dec 12, 2024
1299f1a
wip implementing new feedback envelope capture
armcknight Dec 12, 2024
00ef3dd
necessary tweaks to events/envelopes to get feedback events ingested …
armcknight Dec 13, 2024
5cb9ada
remove some comments to pr/issue
armcknight Dec 13, 2024
4dcee7a
ios-only build requirements needed some places, but actually not for …
armcknight Dec 13, 2024
64ab660
add serialization test
armcknight Dec 13, 2024
df7f177
pr feedback
armcknight Dec 18, 2024
5d1ff6e
wip: intercepting cached envelopes to validate in UI tests
armcknight Dec 18, 2024
f593556
Merge remote-tracking branch 'origin/main' into armcknight/feat(user-…
armcknight Dec 18, 2024
623673c
moving to other branch
armcknight Dec 19, 2024
4b88e4b
remove TestStoreOnlyTransport
armcknight Dec 19, 2024
0c35b26
Merge remote-tracking branch 'origin/main' into armcknight/feat(user-…
armcknight Dec 20, 2024
d5923cf
Update Sentry.xcodeproj/project.pbxproj
armcknight Dec 20, 2024
1e4f7a3
fix pbxproj issue
armcknight Dec 20, 2024
ded938e
test(feedback): validate envelopes (#4652)
armcknight Dec 24, 2024
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
11 changes: 11 additions & 0 deletions Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ class UserFeedbackUITests: BaseUITest {
XCTAssertFalse(app.staticTexts["Thy name (Required)"].exists)
}

func testPrefilledUserInformation() throws {
launchApp(args: ["--io.sentry.feedback.use-sentry-user"], env: [
"--io.sentry.user.name": "ui test user",
"--io.sentry.user.email": "[email protected]"
])

widgetButton.tap()
XCTAssertEqual(try XCTUnwrap(nameField.value as? String), "ui test user")
XCTAssertEqual(try XCTUnwrap(emailField.value as? String), "[email protected]")
}

// MARK: Tests validating happy path / successful submission

func testSubmitFullyFilledForm() throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "--io.sentry.disable-everything"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--disable-file-io-tracing"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--io.sentry.feedback.use-sentry-user"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "--io.sentry.feedback.require-name"
isEnabled = "NO">
Expand Down Expand Up @@ -236,6 +244,11 @@
value = ""
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "--io.sentry.user.name"
value = ""
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
Expand Down
4 changes: 3 additions & 1 deletion Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
user.email = env["--io.sentry.user.email"] ?? "[email protected]"
// first check if the username has been overridden in the scheme for testing purposes; then try to use the system username so each person gets an automatic way to easily filter things on the dashboard; then fall back on a hardcoded value if none of these are present
let username = env["--io.sentry.user.username"] ?? (env["SIMULATOR_HOST_HOME"] as? NSString)?
.lastPathComponent ?? "cocoa developer"
.lastPathComponent ?? "cocoadev"
user.username = username
user.name = env["--io.sentry.user.name"] ?? "cocoa developer"
scope.setUser(user)

if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") {
Expand All @@ -174,6 +175,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return
}
config.animations = !args.contains("--io.sentry.feedback.no-animations")
config.useSentryUser = args.contains("--io.sentry.feedback.use-sentry-user")
config.useShakeGesture = true
config.showFormForScreenshots = true
config.configureWidget = { widget in
Expand Down
12 changes: 12 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@
84CFA4CA2C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */; };
84CFA4CD2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.m */; };
84CFA4CE2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h */; };
84DBC62C2CE82F12000C4904 /* SentryFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DBC62B2CE82F0E000C4904 /* SentryFeedback.swift */; };
84DEE86B2B686BD400A7BC17 /* SentrySamplerDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DEE86A2B686BD400A7BC17 /* SentrySamplerDecision.h */; };
84DEE8762B69AD6400A7BC17 /* SentryLaunchProfiling.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DEE8752B69AD6400A7BC17 /* SentryLaunchProfiling.h */; };
84E13B842CBF1D91003B52EC /* SentryUserFeedbackWidgetButtonMegaphoneIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E13B832CBF1D91003B52EC /* SentryUserFeedbackWidgetButtonMegaphoneIconView.swift */; };
Expand Down Expand Up @@ -1776,6 +1777,7 @@
84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackWidget.swift; sourceTree = "<group>"; };
84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryUserFeedbackIntegration.h; path = ../../../Sentry/include/SentryUserFeedbackIntegration.h; sourceTree = "<group>"; };
84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SentryUserFeedbackIntegration.m; path = ../../../Sentry/SentryUserFeedbackIntegration.m; sourceTree = "<group>"; };
84DBC62B2CE82F0E000C4904 /* SentryFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFeedback.swift; 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; };
84E13B832CBF1D91003B52EC /* SentryUserFeedbackWidgetButtonMegaphoneIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUserFeedbackWidgetButtonMegaphoneIconView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2032,6 +2034,10 @@
D8FFE50B2703DAAE00607131 /* SwizzlingCallTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwizzlingCallTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
843FB3142D0BDA3900558F18 /* Feedback */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Feedback; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */

/* Begin PBXFrameworksBuildPhase section */
63AA75971EB8AEF500D153DE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
Expand Down Expand Up @@ -2957,6 +2963,7 @@
7B944FA924697E9700A10721 /* Integrations */ = {
isa = PBXGroup;
children = (
843FB3142D0BDA3900558F18 /* Feedback */,
7BF6505D292B77D100BBA5A8 /* MetricKit */,
D808FB85281AB2EF009A2A33 /* UIEvents */,
D8AB40D92806EBDC00E5E9F7 /* Screenshot */,
Expand Down Expand Up @@ -3522,6 +3529,7 @@
children = (
849B8F9E2C70091A00148E1F /* Configuration */,
849B8F962C6E906900148E1F /* SentryUserFeedbackIntegrationDriver.swift */,
84DBC62B2CE82F0E000C4904 /* SentryFeedback.swift */,
84CFA4CB2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.h */,
84CFA4CC2C9E0CA3008DA5F4 /* SentryUserFeedbackIntegration.m */,
84CFA4C92C9DF884008DA5F4 /* SentryUserFeedbackWidget.swift */,
Expand Down Expand Up @@ -4348,6 +4356,9 @@
D84DAD5C2B1742C1003CF120 /* PBXTargetDependency */,
D85153032CA2B5F60070F669 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
843FB3142D0BDA3900558F18 /* Feedback */,
);
name = SentryTests;
packageProductDependencies = (
);
Expand Down Expand Up @@ -4564,6 +4575,7 @@
7B6438AB26A70F24000D0F65 /* UIViewController+Sentry.m in Sources */,
84302A812B5767A50027A629 /* SentryLaunchProfiling.m in Sources */,
63AA76A31EB9CBAA00D153DE /* SentryDsn.m in Sources */,
84DBC62C2CE82F12000C4904 /* SentryFeedback.swift in Sources */,
63B818FA1EC34639002FDF4C /* SentryDebugMeta.m in Sources */,
7B98D7D325FB65AE00C5A389 /* SentryWatchdogTerminationTracker.m in Sources */,
8E564AE8267AF22600FE117D /* SentryNetworkTrackingIntegration.m in Sources */,
Expand Down
31 changes: 10 additions & 21 deletions Sources/Sentry/Public/SentrySDK.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@

@protocol SentrySpan;

@class SentryOptions, SentryEvent, SentryBreadcrumb, SentryScope, SentryUser, SentryId,
SentryUserFeedback, SentryTransactionContext;
@class SentryBreadcrumb;
@class SentryEvent;
@class SentryFeedback;
@class SentryId;
@class SentryMetricsAPI;
@class UIView;
@class SentryOptions;
@class SentryReplayApi;
@class SentryScope;
@class SentryTransactionContext;
@class SentryUser;
@class SentryUserFeedback;
@class UIView;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -246,28 +253,10 @@ SENTRY_NO_INIT
/**
* Captures user feedback that was manually gathered and sends it to Sentry.
* @param userFeedback The user feedback to send to Sentry.
* @note If you'd prefer not to have to build the UI required to gather the feedback from the user,
* consider using `showUserFeedbackForm`, which delivers a prepackaged user feedback experience. See
* @c SentryOptions.configureUserFeedback to customize a fully managed integration. See
* https://docs.sentry.io/platforms/apple/user-feedback/#user-feedback-api and (TODO: add link to
* new docs) for more information on each approach.
*/
+ (void)captureUserFeedback:(SentryUserFeedback *)userFeedback
NS_SWIFT_NAME(capture(userFeedback:));

/**
* Display a form to gather information from an end user in the app to send to Sentry as a user
* feedback event.
* @see @c SentryOptions.enableUserFeedbackIntegration and @c SentryOptions.configureUserFeedback to
* enable the functionality and customize the experience.
* @note If @c SentryOptions.enableUserFeedbackIntegration is @c NO, this method is a no-op.
* @note This is a fully managed user feedback flow; there will be no need to call
* @c SentrySDK.captureUserFeedback . See
* https://docs.sentry.io/platforms/apple/user-feedback/#user-feedback-api and (TODO: add link to
* new docs) for more information on each approach.
*/
+ (void)showUserFeedbackForm;

/**
* Adds a Breadcrumb to the current Scope of the current Hub. If the total number of breadcrumbs
* exceeds the @c SentryOptions.maxBreadcrumbs the SDK removes the oldest breadcrumb.
Expand Down
58 changes: 42 additions & 16 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,7 @@ - (SentryId *)sendEvent:(SentryEvent *)event

SentryTraceContext *traceContext = [self getTraceStateWithEvent:event withScope:scope];

NSArray *attachments = scope.attachments;
if (self.attachmentProcessors.count) {
for (id<SentryClientAttachmentProcessor> attachmentProcessor in self.attachmentProcessors) {
attachments = [attachmentProcessor processAttachments:attachments
forEvent:preparedEvent];
}
}
NSArray *attachments = [self attachmentsForEvent:preparedEvent scope:scope];

[self.transportAdapter sendEvent:preparedEvent
traceContext:traceContext
Expand All @@ -473,18 +467,23 @@ - (SentryId *)sendEvent:(SentryEvent *)event
return preparedEvent.eventId;
}

- (NSArray *)attachmentsForEvent:(SentryEvent *)event scope:(SentryScope *)scope
armcknight marked this conversation as resolved.
Show resolved Hide resolved
{
NSArray *attachments = scope.attachments;
if (self.attachmentProcessors.count) {
for (id<SentryClientAttachmentProcessor> attachmentProcessor in self.attachmentProcessors) {
attachments = [attachmentProcessor processAttachments:attachments forEvent:event];
}
}
return attachments;
}

- (SentryId *)sendEvent:(SentryEvent *)event
withSession:(SentrySession *)session
withScope:(SentryScope *)scope
{
if (nil != event) {
NSArray *attachments = scope.attachments;
if (self.attachmentProcessors.count) {
for (id<SentryClientAttachmentProcessor> attachmentProcessor in self
.attachmentProcessors) {
attachments = [attachmentProcessor processAttachments:attachments forEvent:event];
}
}
NSArray *attachments = [self attachmentsForEvent:event scope:scope];

if (event.isCrashEvent && event.context[@"replay"] &&
[event.context[@"replay"] isKindOfClass:NSDictionary.class]) {
Expand Down Expand Up @@ -585,6 +584,30 @@ - (void)captureUserFeedback:(SentryUserFeedback *)userFeedback
[self.transportAdapter sendUserFeedback:userFeedback];
}

- (void)captureFeedback:(SentryFeedback *)feedback withScope:(SentryScope *)scope
armcknight marked this conversation as resolved.
Show resolved Hide resolved
{
SentryEvent *feedbackEvent = [[SentryEvent alloc] init];
feedbackEvent.eventId = feedback.eventId;
feedbackEvent.type = SentryEnvelopeItemTypeFeedback;

NSDictionary *serializedFeedback = [feedback serialize];

NSMutableDictionary *context = [NSMutableDictionary dictionary];
context[@"feedback"] = serializedFeedback;
feedbackEvent.context = context;
armcknight marked this conversation as resolved.
Show resolved Hide resolved

SentryEvent *preparedEvent = [self prepareEvent:feedbackEvent
withScope:scope
alwaysAttachStacktrace:NO];
SentryTraceContext *traceContext = [self getTraceStateWithEvent:preparedEvent withScope:scope];
NSArray *attachments = [[self attachmentsForEvent:preparedEvent scope:scope]
arrayByAddingObjectsFromArray:[feedback attachments]];
[self.transportAdapter sendEvent:preparedEvent
traceContext:traceContext
attachments:attachments
additionalEnvelopeItems:@[]];
armcknight marked this conversation as resolved.
Show resolved Hide resolved
}

- (void)storeEnvelope:(SentryEnvelope *)envelope
{
[self.fileManager storeEnvelope:envelope];
Expand Down Expand Up @@ -643,6 +666,8 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
= event.type == nil || ![event.type isEqualToString:SentryEnvelopeItemTypeTransaction];
BOOL eventIsNotReplay
= event.type == nil || ![event.type isEqualToString:SentryEnvelopeItemTypeReplayVideo];
BOOL eventIsNotUserFeedback
= event.type == nil || ![event.type isEqualToString:SentryEnvelopeItemTypeFeedback];

// Transactions and replays have their own sampleRate
if (eventIsNotATransaction && eventIsNotReplay && [self isSampled:self.options.sampleRate]) {
Expand Down Expand Up @@ -672,8 +697,9 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event

[self setSdk:event];

// We don't want to attach debug meta and stacktraces for transactions and replays.
if (eventIsNotATransaction && eventIsNotReplay) {
// We don't want to attach debug meta and stacktraces for transactions, replays or user
// feedback.
if (eventIsNotATransaction && eventIsNotReplay && eventIsNotUserFeedback) {
BOOL shouldAttachStacktrace = alwaysAttachStacktrace || self.options.attachStacktrace
|| (nil != event.exceptions && [event.exceptions count] > 0);

Expand Down
20 changes: 14 additions & 6 deletions Sources/Sentry/SentryDataCategoryMapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
NSString *const kSentryDataCategoryNameReplay = @"replay";
NSString *const kSentryDataCategoryNameMetricBucket = @"metric_bucket";
NSString *const kSentryDataCategoryNameSpan = @"span";
NSString *const kSentryDataCategoryNameFeedback = @"feedback";
armcknight marked this conversation as resolved.
Show resolved Hide resolved
NSString *const kSentryDataCategoryNameUnknown = @"unknown";

NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -46,6 +47,9 @@
if ([itemType isEqualToString:SentryEnvelopeItemTypeReplayVideo]) {
return kSentryDataCategoryReplay;
}
if ([itemType isEqualToString:SentryEnvelopeItemTypeFeedback]) {
return kSentryDataCategoryFeedback;
}
// The envelope item type used for metrics is statsd whereas the client report category for
// discarded events is metric_bucket.
if ([itemType isEqualToString:SentryEnvelopeItemTypeStatsd]) {
Expand Down Expand Up @@ -104,20 +108,20 @@
if ([value isEqualToString:kSentryDataCategoryNameSpan]) {
return kSentryDataCategorySpan;
}
if ([value isEqualToString:kSentryDataCategoryNameFeedback]) {
return kSentryDataCategoryFeedback;
}

return kSentryDataCategoryUnknown;
}

NSString *
nameForSentryDataCategory(SentryDataCategory category)
{
if (category < kSentryDataCategoryAll && category > kSentryDataCategoryUnknown) {
return kSentryDataCategoryNameUnknown;
}

switch (category) {
case kSentryDataCategoryAll:
return kSentryDataCategoryNameAll;

case kSentryDataCategoryDefault:
return kSentryDataCategoryNameDefault;
case kSentryDataCategoryError:
Expand All @@ -136,12 +140,16 @@
return kSentryDataCategoryNameProfileChunk;
case kSentryDataCategoryMetricBucket:
return kSentryDataCategoryNameMetricBucket;
case kSentryDataCategoryUnknown:
return kSentryDataCategoryNameUnknown;
case kSentryDataCategoryReplay:
return kSentryDataCategoryNameReplay;
case kSentryDataCategorySpan:
return kSentryDataCategoryNameSpan;
case kSentryDataCategoryFeedback:
return kSentryDataCategoryNameFeedback;

default: // !!!: fall-through!
case kSentryDataCategoryUnknown:
return kSentryDataCategoryNameUnknown;
}
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/SentryEnvelope.m
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ - (instancetype)initWithEvent:(SentryEvent *)event
// default. In any case in the envelope type it should be event. Except for transactions
NSString *envelopeType = [event.type isEqualToString:SentryEnvelopeItemTypeTransaction]
? SentryEnvelopeItemTypeTransaction
: [event.type isEqualToString:SentryEnvelopeItemTypeFeedback]
armcknight marked this conversation as resolved.
Show resolved Hide resolved
? SentryEnvelopeItemTypeFeedback
: SentryEnvelopeItemTypeEvent;

return [self initWithHeader:[[SentryEnvelopeItemHeader alloc] initWithType:envelopeType
Expand Down
8 changes: 8 additions & 0 deletions Sources/Sentry/SentryHub.m
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,14 @@ - (void)captureUserFeedback:(SentryUserFeedback *)userFeedback
}
}

- (void)captureFeedback:(SentryFeedback *)feedback
{
SentryClient *client = _client;
if (client != nil) {
[client captureFeedback:feedback withScope:self.scope];
}
}

- (void)addBreadcrumb:(SentryBreadcrumb *)crumb
{
SentryOptions *options = [[self client] options];
Expand Down
7 changes: 7 additions & 0 deletions Sources/Sentry/SentrySDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -405,10 +405,17 @@ + (void)captureUserFeedback:(SentryUserFeedback *)userFeedback
[SentrySDK.currentHub captureUserFeedback:userFeedback];
}

+ (void)captureFeedback:(SentryFeedback *)feedback
{
[SentrySDK.currentHub captureFeedback:feedback];
}

#if TARGET_OS_IOS && SENTRY_HAS_UIKIT
+ (void)showUserFeedbackForm
{
// TODO: implement
}
#endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT

+ (void)addBreadcrumb:(SentryBreadcrumb *)crumb
{
Expand Down
1 change: 1 addition & 0 deletions Sources/Sentry/SentryTransportAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import "SentryEnvelope.h"
#import "SentryEvent.h"
#import "SentryOptions.h"
#import "SentrySwift.h"
armcknight marked this conversation as resolved.
Show resolved Hide resolved
#import "SentryUserFeedback.h"
#import <Foundation/Foundation.h>

Expand Down
Loading
Loading