---
title: "User Feedback"
description: "Learn how to enable User Feedback in your Cocoa app."
url: https://docs.sentry.io/platforms/apple/guides/ios/user-feedback/
---

# Set Up User Feedback | Sentry for iOS

User Feedback lets you collect feedback from anywhere in your app without waiting for an error event. On supported platforms, you can use Sentry's managed feedback form, or you can collect feedback in your own UI and send it with the User Feedback API.

If you use a self-hosted Sentry instance, use version 24.4.2 or higher for the managed feedback form. Lower versions may have limited functionality.

## [Managed Feedback Form](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#managed-feedback-form)

The managed feedback form is available for iOS and iPadOS apps that can use UIKit. SwiftUI apps can present the same form through Sentry's SwiftUI helpers.

The managed form presentation APIs are experimental and aren't available in app extensions.

### [Prerequisites](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#prerequisites)

1. Set up Sentry in your app.
2. Use the latest Sentry Cocoa SDK. The Swift presentation APIs require version 9.17.0 or newer.
3. Configure User Feedback when starting the SDK.

The Objective-C snippets on this page use the [SentryObjC](https://docs.sentry.io/platforms/apple/guides/ios/install/swift-package-manager.md#sentryobjc) API.

### [Configure User Feedback](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#configure-user-feedback)

Set `configureUserFeedback` during SDK initialization. This closure installs the User Feedback integration, which is required for the automatic `SentrySDK.feedback.show()` API and for global shake and screenshot triggers. It also provides the global configuration used by `SentrySDK.feedback.show()`, `SentrySDK.FeedbackForm`, and `.sentryFeedback(isPresented:)`.

**Swift**

```swift
import Sentry

SentrySDK.start { options in
    options.dsn = "https://<key>@o<orgId>.ingest.sentry.io/<projectId>"
    options.configureUserFeedback = { config in
        config.configureForm = { form in
            form.formTitle = "Report Feedback"
            form.submitButtonLabel = "Send Feedback"
        }
        config.onSubmitSuccess = { data in
            print("Feedback submitted: \(data)")
        }
        config.onSubmitError = { error in
            print("Feedback failed: \(error)")
        }
    }
}
```

**Objective-C**

```objc
@import SentryObjC;

[SentryObjCSDK startWithConfigureOptions:^(SentryObjCOptions *options) {
    options.dsn = @"https://<key>@o<orgId>.ingest.sentry.io/<projectId>";
    options.configureUserFeedback = ^(SentryObjCUserFeedbackConfiguration *config) {
        config.configureForm = ^(SentryObjCUserFeedbackFormConfiguration *form) {
            form.formTitle = @"Report Feedback";
            form.submitButtonLabel = @"Send Feedback";
        };
        config.onSubmitSuccess = ^(NSDictionary<NSString *, id> *data) {
            NSLog(@"Feedback submitted: %@", data);
        };
        config.onSubmitError = ^(NSError *error) {
            NSLog(@"Feedback failed: %@", error);
        };
    };
}];
```

For all configuration fields, see [Configure User Feedback](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback/configuration.md).

With global configuration, the SDK tracks the active managed form and won't present another form from SDK-managed presentation paths while one is already open.

If you create `SentrySDK.FeedbackForm` or `SentrySDK.FeedbackFormView` directly without global configuration, the form uses default settings plus any per-presentation configuration you provide.

### [Present the Form](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#present-the-form)

Choose between programmatic presentation APIs and automatic triggers based on how much control your app needs:

* Use `SentrySDK.feedback.show()` when the SDK can choose the presenter from the active scene.
* Use the UIKit or SwiftUI APIs when your app needs exact control over the presenting view controller, scene, window, or sheet.
* Use shake and screenshot triggers when the form should open from a global user action.

#### [Show From the Active Screen](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#show-from-the-active-screen)

Use `SentrySDK.feedback.show()` when you want the SDK to find a suitable view controller in the active scene and present the form for you.

**Swift**

```swift
SentrySDK.feedback.show { config in
    config.configureForm = { form in
        form.formTitle = "Feedback for this screen"
    }
}
```

**Objective-C**

```objc
[[SentryObjCSDK feedback] showWithConfigure:^(SentryObjCUserFeedbackConfiguration *config) {
    config.configureForm = ^(SentryObjCUserFeedbackFormConfiguration *form) {
        form.formTitle = @"Feedback for this screen";
    };
}];
```

You can also pass a `UIImage` screenshot:

**Swift**

```swift
SentrySDK.feedback.show(screenshot: screenshot)
```

**Objective-C**

```objc
[[SentryObjCSDK feedback] showWithScreenshot:screenshot];
```

The SDK chooses a presenting view controller from the foreground-active scene when possible, with a window fallback for apps that don't use scenes. It only presents when that view controller isn't already presenting or transitioning.

If your app needs exact scene, window, or presenting view controller control, use the UIKit or SwiftUI APIs below to present the form yourself.

#### [Present From UIKit](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#present-from-uikit)

Use `SentrySDK.FeedbackForm` when your app should decide which view controller presents the form.

**Swift**

```swift
let form = SentrySDK.FeedbackForm(screenshot: screenshot) { config in
    config.configureForm = { form in
        form.formTitle = "Report Feedback"
        form.submitButtonLabel = "Send Feedback"
    }
}

present(form, animated: true)
```

**Objective-C**

```objc
UIViewController *form = [SentryObjCFeedbackForm
    viewControllerWithScreenshot:screenshot
                       configure:^(SentryObjCUserFeedbackConfiguration *config) {
    config.configureForm = ^(SentryObjCUserFeedbackFormConfiguration *formConfig) {
        formConfig.formTitle = @"Report Feedback";
        formConfig.submitButtonLabel = @"Send Feedback";
    };
}];

[self presentViewController:form animated:YES completion:nil];
```

#### [Present From SwiftUI](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#present-from-swiftui)

Use `.sentryFeedback(isPresented:)` when a SwiftUI state binding should control the form presentation.

```swift
import Sentry
import SwiftUI

struct SettingsView: View {
    @State private var isFeedbackPresented = false

    var body: some View {
        Button("Send Feedback") {
            isFeedbackPresented = true
        }
        .sentryFeedback(isPresented: $isFeedbackPresented) { config in
            config.configureForm = { form in
                form.submitButtonLabel = "Send Feedback"
            }
        }
    }
}
```

If you already manage your own presentation container, embed `SentrySDK.FeedbackFormView` in a SwiftUI sheet:

```swift
import Sentry
import SwiftUI

struct SettingsView: View {
    @State private var isFeedbackPresented = false

    var body: some View {
        Button("Send Feedback") {
            isFeedbackPresented = true
        }
        .sheet(isPresented: $isFeedbackPresented) {
            SentrySDK.FeedbackFormView { config in
                config.configureForm = { form in
                    form.submitButtonLabel = "Send Feedback"
                }
            }
        }
    }
}
```

#### [Open Automatically From a Shake Gesture or Screenshot](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#open-automatically-from-a-shake-gesture-or-screenshot)

You can also open the managed form from a device shake or immediately after the user takes a screenshot. These options are global and only apply when set in `options.configureUserFeedback`.

**Swift**

```swift
SentrySDK.start { options in
    options.dsn = "https://<key>@o<orgId>.ingest.sentry.io/<projectId>"
    options.configureUserFeedback = { config in
        config.useShakeGesture = true
        config.showFormForScreenshots = true
    }
}
```

**Objective-C**

```objc
[SentryObjCSDK startWithConfigureOptions:^(SentryObjCOptions *options) {
    options.dsn = @"https://<key>@o<orgId>.ingest.sentry.io/<projectId>";
    options.configureUserFeedback = ^(SentryObjCUserFeedbackConfiguration *config) {
        config.useShakeGesture = YES;
        config.showFormForScreenshots = YES;
    };
}];
```

### [Session Replay](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#session-replay)

The managed feedback form works with [Session Replay](https://docs.sentry.io/platforms/apple/guides/ios/session-replay.md). When the form opens, the Replay SDK buffers up to 30 seconds of the user's session. If the user submits feedback, the replay is sent with the feedback so you can see what led up to it.

### [Deprecated Widget and Custom Button Setup](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#deprecated-widget-and-custom-button-setup)

The Sentry-managed User Feedback widget, `showWidget()`, `hideWidget()`, `configureWidget`, and `customButton` configuration are deprecated and will be removed in v10.

If you're migrating from `customButton`, keep your existing button and replace the SDK configuration with a regular button action. If you're migrating from the widget, add your own button, menu item, or other entry point and call the same presentation API. Your global `configureUserFeedback` form, theme, and hook configuration still applies.

**Swift**

```swift
@IBAction private func feedbackButtonTapped(_ sender: UIButton) {
    SentrySDK.feedback.show()
}
```

**Objective-C**

```objc
- (IBAction)feedbackButtonTapped:(UIButton *)sender {
    [[SentryObjCSDK feedback] show];
}
```

## [User Feedback API](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback.md#user-feedback-api)

Use the User Feedback API when you collect feedback with your own UI. The SDK sends the feedback to Sentry, so you don't need to build the HTTP request yourself.

You can optionally pass an `associatedEventId` to connect feedback to an error event.

**Swift**

```swift
import Sentry

let feedback = SentryFeedback(
    message: "The settings screen is confusing.",
    name: "Ada",
    email: "ada@example.com",
    source: .custom,
    associatedEventId: nil,
    attachments: nil
)

SentrySDK.capture(feedback: feedback)
```

**Objective-C**

```objc
@import SentryObjC;

[SentryObjCSDK captureFeedbackWithMessage:@"The settings screen is confusing."
                                     name:@"Ada"
                                    email:@"ada@example.com"
                                   source:SentryObjCFeedbackSourceCustom
                        associatedEventId:nil
                              attachments:nil];
```

## Pages in this section

- [Configure User Feedback](https://docs.sentry.io/platforms/apple/guides/ios/user-feedback/configuration.md)
