> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appdna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS Surveys & Feedback

> Collect NPS, CSAT, and custom feedback with 8 question types

<Info>
  **Supported on:** iOS SDK `1.0.61+`
</Info>

<Tip>
  You can A/B test this survey with no extra code — create an experiment on it in the Console and the SDK serves the assigned variant automatically. See [Servable Surface Experiments](/sdks/ios/experiments#servable-surface-experiments).
</Tip>

The surveys module lets you collect user feedback through in-app surveys. Surveys can trigger automatically based on events (like in-app messages) or be presented manually from your code.

## How It Works

Surveys are configured in the Console with content, question types, and trigger rules. Like in-app messages, they can appear automatically when trigger conditions are met -- no code required for trigger-based surveys.

## Present a Survey Manually

```swift theme={null}
AppDNA.surveys.present("nps_q1_2026")
```

## Module Access

```swift theme={null}
let surveys = AppDNA.surveys
```

### Module Methods

| Method        | Signature                                        | Description                         |
| ------------- | ------------------------------------------------ | ----------------------------------- |
| `present`     | `present(_ surveyId: String)`                    | Present a survey manually           |
| `setDelegate` | `setDelegate(_ delegate: AppDNASurveyDelegate?)` | Set a delegate for survey callbacks |

## 8 Question Types

Surveys support the following question types, configured in the Console:

| Type            | Display                | Response                  |
| --------------- | ---------------------- | ------------------------- |
| `nps`           | 0-10 numeric scale     | Integer 0-10              |
| `csat`          | 1-5 satisfaction scale | Integer 1-5               |
| `rating`        | Star rating            | Integer 1-5               |
| `emoji_scale`   | Emoji options          | Selected emoji value      |
| `yes_no`        | Binary choice          | Boolean                   |
| `single_choice` | Radio button options   | Selected option string    |
| `multi_choice`  | Checkbox options       | Array of selected strings |
| `free_text`     | Text input field       | Free-form string          |

<Info>
  Question types and their content are defined entirely in the Console. The SDK renders them automatically based on the survey configuration.
</Info>

## AppDNASurveyDelegate

All 3 methods on this delegate fire from `SurveyManager` alongside the existing analytics events. Register your delegate via `AppDNA.surveys.setDelegate(...)`.

* `onSurveyPresented(surveyId:)` — fires when the survey view appears (alongside the `survey_shown` event).
* `onSurveyCompleted(surveyId:, responses:)` — fires when the user submits the final question. `responses` is the public `[SurveyResponse]` array (one entry per answered question with `questionId` + `answer`), suitable for forwarding to your own analytics or CRM.
* `onSurveyDismissed(surveyId:)` — fires when the user closes the survey before completing it (X button, swipe-to-dismiss, outside tap).

Implement the delegate to capture survey responses and lifecycle events:

```swift theme={null}
protocol AppDNASurveyDelegate {
    func onSurveyPresented(surveyId: String)
    func onSurveyCompleted(surveyId: String, responses: [SurveyResponse])
    func onSurveyDismissed(surveyId: String)
}
```

### SurveyResponse

```swift theme={null}
struct SurveyResponse {
    let questionId: String
    let answer: Any  // String, Int, [String], Bool depending on question type
}
```

### Example Implementation

```swift theme={null}
class FeedbackHandler: AppDNASurveyDelegate {
    func onSurveyPresented(surveyId: String) {
        print("Survey shown: \(surveyId)")
    }

    func onSurveyCompleted(surveyId: String, responses: [SurveyResponse]) {
        for response in responses {
            if let score = response.answer as? Int {
                if score >= 9 {
                    // Promoter -- show referral prompt
                    showReferralPrompt()
                } else if score <= 6 {
                    // Detractor -- show support link
                    showSupportLink()
                }
            } else if let feedback = response.answer as? String {
                print("User feedback: \(feedback)")
            }
        }
    }

    func onSurveyDismissed(surveyId: String) {
        print("Survey dismissed: \(surveyId)")
    }
}

AppDNA.surveys.setDelegate(FeedbackHandler())
```

## Rich Media

Survey questions support rich media content configured in the Console:

* **Header images** -- add images above survey questions
* **Icons in options** -- use icon references in choice options (Lucide, SF Symbols, Material, or emoji)
* **Thank-you animations** -- Lottie or confetti effects on survey completion
* **Haptic feedback** -- triggered on option selection and submission

See the [Rich Media](/sdks/ios/rich-media) guide for details on supported formats.

## Auto-Tracked Events

| Event              | Trigger                             |
| ------------------ | ----------------------------------- |
| `survey_shown`     | A survey is displayed               |
| `survey_completed` | User submits a survey response      |
| `survey_dismissed` | Survey is closed without completing |

## Full Example

```swift theme={null}
import AppDNASDK

class FeedbackManager: AppDNASurveyDelegate {
    init() {
        AppDNA.surveys.setDelegate(self)
    }

    /// Present NPS survey after a key moment
    func askForFeedback() {
        AppDNA.surveys.present("nps_q1_2026")
    }

    // MARK: - AppDNASurveyDelegate

    func onSurveyPresented(surveyId: String) {
        // Survey is visible
    }

    func onSurveyCompleted(surveyId: String, responses: [SurveyResponse]) {
        // React to feedback
        if let nps = responses.first(where: { $0.questionId == "nps_question" }),
           let score = nps.answer as? Int {
            if score >= 9 {
                showReferralPrompt()
            } else if score <= 6 {
                showSupportLink()
            }
        }
    }

    func onSurveyDismissed(surveyId: String) {
        // User dismissed without responding
    }
}
```

<Note>
  Surveys are created in the Console under **Feedback > Surveys**. Trigger-based surveys appear automatically -- manual presentation with `present()` is for cases where you want precise control over timing.
</Note>
