# API reference

> Every public type, property and method in the Grovs iOS SDK 3.1.

Source: https://docs.grovs.io/docs/sdk/ios/api-reference

All members are static on `Grovs`, except the [extension](#extensions) types. Completions and delegate callbacks run on the main thread.

## At a glance

| Area                   | Member                                                                           | Kind     | What it does                          |
| ---------------------- | -------------------------------------------------------------------------------- | -------- | ------------------------------------- |
| **Setup**              | [`configure`](#configure)                                                        | method   | Configures and authenticates the SDK  |
|                        | [`delegate`](#delegate)                                                          | property | Receives deep links and SDK errors    |
|                        | [`setSDK`](#setsdk)                                                              | method   | Turns collection on or off            |
|                        | [`setDebug`](#setdebug)                                                          | method   | Sets the minimum log level            |
| **Deep links**         | [`handleSceneDelegate(openURLContexts:)`](#handlescenedelegate-openurlcontexts)  | method   | Forwards scene URL opens              |
|                        | [`handleSceneDelegate(continue:)`](#handlescenedelegate-continue)                | method   | Forwards scene universal links        |
|                        | [`handleSceneDelegate(options:)`](#handlescenedelegate-options)                  | method   | Forwards the cold-start link          |
|                        | [`handleAppDelegate(continue:restorationHandler:)`](#handleappdelegate-continue) | method   | Forwards app delegate universal links |
|                        | [`handleAppDelegate(open:options:)`](#handleappdelegate-open)                    | method   | Forwards app delegate URL opens       |
|                        | [`lastReceivedPayload`](#lastreceivedpayload)                                    | method   | Most recent deep link payload         |
|                        | [`allReceivedPayloadsSinceStartup`](#allreceivedpayloadssincestartup)            | method   | Every payload since launch            |
| **Users**              | [`userIdentifier`](#useridentifier)                                              | property | Your ID for the current user          |
|                        | [`userAttributes`](#userattributes)                                              | property | Attributes of the current user        |
| **Links**              | [`generateLink`](#generatelink)                                                  | method   | Creates a smart link                  |
|                        | [`linkDetails`](#linkdetails)                                                    | method   | Details of a link by path             |
| **Events**             | [`track`](#track)                                                                | method   | Tracks a custom event                 |
|                        | [`trackScreenView`](#trackscreenview)                                            | method   | Tracks a screen view                  |
|                        | [`setGlobalTags`](#setglobaltags)                                                | method   | Tags added to every event             |
|                        | [`setScreenAliases`](#setscreenaliases)                                          | method   | Renames screens for the dashboard     |
|                        | [`screenNameProvider`](#screennameprovider)                                      | property | Custom resolver for screen names      |
| **Revenue**            | [`logInAppPurchase`](#loginapppurchase)                                          | method   | Logs an App Store purchase            |
|                        | [`logCustomPurchase`](#logcustompurchase)                                        | method   | Logs a purchase outside the App Store |
| **Push notifications** | [`pushToken`](#pushtoken)                                                        | property | APNs token as a hex string            |
|                        | [`setPushToken`](#setpushtoken)                                                  | method   | Sets the token from APNs `Data`       |
|                        | [`handleNotificationResponse`](#handlenotificationresponse)                      | method   | Handles a notification tap            |
|                        | [`handleNotificationOpen`](#handlenotificationopen)                              | method   | Handles a tap from its payload        |
|                        | [`isGrovsNotification`](#isgrovsnotification)                                    | method   | Checks if Grovs sent a push           |
| **In-app messages**    | [`inAppDelegate`](#inappdelegate)                                                | property | Receives in-app message events        |
|                        | [`pauseInAppMessages`](#pauseinappmessages)                                      | method   | Holds messages back                   |
|                        | [`resumeInAppMessages`](#resumeinappmessages)                                    | method   | Lets messages show again              |
|                        | [`discardInAppMessages`](#discardinappmessages)                                  | method   | Drops the waiting message             |
|                        | [`setInAppExcludedScreens`](#setinappexcludedscreens)                            | method   | Screens where no message shows        |
|                        | [`setInAppDebugLogging`](#setinappdebuglogging)                                  | method   | Logs why campaigns were skipped       |
| **Types**              | [`GrovsDelegate`](#grovsdelegate)                                                | protocol | Deep link and error callbacks         |
|                        | [`GrovsError`](#grovserror)                                                      | enum     | SDK error categories                  |
|                        | [`GrovsInAppDelegate`](#grovsinappdelegate)                                      | protocol | In-app message callbacks              |
|                        | [`GrovsScreenTracking`](#grovsscreentracking)                                    | protocol | Names a view controller               |
|                        | [`GrovsScreenName`](#grovsscreenname)                                            | enum     | Result of `screenNameProvider`        |
|                        | [`LogLevel`](#loglevel)                                                          | enum     | Log verbosity                         |
|                        | [`TransactionType`](#transactiontype)                                            | enum     | Buy, cancel or refund                 |
|                        | [`CustomRedirects`](#customredirects)                                            | type     | Per-platform link redirects           |
|                        | [`CustomLinkRedirect`](#customlinkredirect)                                      | type     | One platform redirect                 |
|                        | [Closure types](#closure-types)                                                  | callback | Completion signatures                 |
| **Extensions**         | [`GrovsNotificationServiceExtension`](#grovsnotificationserviceextension)        | class    | Adds campaign images to pushes        |
|                        | [`GrovsNotificationContent`](#grovsnotificationcontent)                          | enum     | Image helper for existing extensions  |
|                        | [`grovsScreen`](#grovsscreen)                                                    | method   | SwiftUI screen view modifier          |
|                        | [`trackScreenView` (modifier)](#trackscreenview-modifier)                        | method   | Alias of `grovsScreen`                |
| **Deprecated**         | [`displayMessagesViewController`](#displaymessagesviewcontroller)                | method   | Shows nothing                         |
|                        | [`numberOfUnreadMessages`](#numberofunreadmessages)                              | method   | Always `0`                            |

---

## Setup

### configure

Kind: method

```swift
static func configure(
    APIKey: String,
    useTestEnvironment: Bool,
    baseURL: String? = nil,
    autoTrackScreenViews: Bool = true,
    clipboardDomains: [String]? = nil,
    enabled: Bool = true,
    delegate: GrovsDelegate?,
    completion: GrovsBoolCompletion? = nil
)
```

Configures and authenticates the SDK. Call once per launch, in `application(_:didFinishLaunchingWithOptions:)`.

| Name                   | Type                   | Description                                                                                                                                        |
| ---------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `APIKey`               | `String`               | Project API key from the Grovs dashboard.                                                                                                          |
| `useTestEnvironment`   | `Bool`                 | `true` targets the test environment, `false` production.                                                                                           |
| `baseURL`              | `String?`              | Domain of a self-hosted backend, for example `"https://sdk.example.com"`. The SDK appends the API path. Defaults to the Grovs cloud.               |
| `autoTrackScreenViews` | `Bool`                 | Track screen views automatically by hooking `viewDidAppear`. Defaults to `true`.                                                                   |
| `clipboardDomains`     | `[String]?`            | Extra link hosts accepted by clipboard-assisted deferred deep linking, for example `["links.example.com"]`. Grovs-owned hosts are always accepted. |
| `enabled`              | `Bool`                 | Whether the SDK may collect and send anything this launch. Defaults to `true`.                                                                     |
| `delegate`             | `GrovsDelegate?`       | Receives deep link payloads and SDK errors. Held weakly.                                                                                           |
| `completion`           | `GrovsBoolCompletion?` | Called on the main thread with whether authentication succeeded.                                                                                   |

- Calling it again replaces the previous configuration.
- `enabled` isn't persisted. Pass your stored consent value every launch. With `enabled: false` the completion reports `false` without attempting authentication.
- An invalid `baseURL`, or one without a host, refuses configuration: the completion reports `false` and the SDK stays unconfigured.
- On a background launch before the device's first unlock since reboot, configuration waits until the device is unlocked or the app becomes active. Consent, `userIdentifier`, `userAttributes`, global tags and screen aliases set meanwhile are applied then.

Calls made before `configure`:

| Calls                                                                | Effect                           |
| -------------------------------------------------------------------- | -------------------------------- |
| [Deep link handlers](#deep-links), notification taps, `pushToken`    | Held until `configure` runs      |
| `setDebug`, `screenNameProvider`, [in-app methods](#in-app-messages) | Applied immediately              |
| `userIdentifier`, `userAttributes`, `track`, `trackScreenView`       | Ignored, logs an error           |
| `setGlobalTags`, `setScreenAliases`                                  | Ignored silently                 |
| Methods with a completion                                            | Completion gets `nil` or `false` |

### delegate

Kind: property

```swift
static var delegate: GrovsDelegate?
```

Receives deep link payloads and SDK errors. See [`GrovsDelegate`](#grovsdelegate).

- Held **weakly**. Keep a strong reference elsewhere.
- Setting it before `configure` has no effect. Pass it to `configure` or set it afterwards.
- Read it on the main thread.

### setSDK

Kind: method

```swift
static func setSDK(enabled: Bool)
```

Turns collection on or off at runtime, for example when consent changes. See [Privacy & Consent](https://docs.grovs.io/docs/sdk/ios/privacy).

- While disabled, the SDK doesn't authenticate, resolve the device, or send events, screen views or purchases.
- It holds `userIdentifier`, `userAttributes`, `pushToken` and screen alias updates. Events and purchases on disk stay there.
- Enabling authenticates if needed and sends everything held.

### setDebug

Kind: method

```swift
static func setDebug(level: LogLevel)
```

Sets the minimum [log level](#loglevel). Default `.error`.

- Works before `configure`.
- Logs go through `os_log`. In release builds, message content is private and shows as `<private>` in Console.app.

---

## Deep links

Forward each system entry point to Grovs. Links received before `configure` are held until it runs. See [Deep Linking](https://docs.grovs.io/docs/sdk/ios/deep-linking).

### handleSceneDelegate (openURLContexts)

Kind: method

```swift
static func handleSceneDelegate(openURLContexts URLContexts: Set<UIOpenURLContext>)
```

Forward from `scene(_:openURLContexts:)`.

### handleSceneDelegate (continue)

Kind: method

```swift
static func handleSceneDelegate(continue userActivity: NSUserActivity)
```

Forward from `scene(_:continue:)`.

### handleSceneDelegate (options)

Kind: method

```swift
static func handleSceneDelegate(options connectionOptions: UIScene.ConnectionOptions)
```

Forward from `scene(_:willConnectTo:options:)`. Handles a link that cold-started the app.

### handleAppDelegate (continue)

Kind: method

```swift
static func handleAppDelegate(
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool
```

Forward from `application(_:continue:restorationHandler:)` and return its result.

- Main thread only. UIKit already calls your app delegate there.

### handleAppDelegate (open)

Kind: method

```swift
static func handleAppDelegate(
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool
```

Forward from `application(_:open:options:)` and return its result.

- Main thread only.
- Returns `false` for a custom scheme other than your project's Grovs URL scheme, such as a sign-in callback, so another handler after Grovs in an `||` chain still gets it. Grovs still looks it up, without its query and fragment.

### lastReceivedPayload

Kind: method

```swift
static func lastReceivedPayload(completion: @escaping GrovsPayloadClosure)
```

Gets the most recent deep link payload, or `nil`.

### allReceivedPayloadsSinceStartup

Kind: method

```swift
static func allReceivedPayloadsSinceStartup(completion: @escaping GrovsPayloadsClosure)
```

Gets every payload received since the app launched, or `nil`.

---

## Users

### userIdentifier

Kind: property

```swift
static var userIdentifier: String?
```

Your identifier for the current user, typically your `user_id`. Shown in the dashboard and reports.

- Set it after `configure`.
- While the SDK is disabled, changes are held until it's enabled.

### userAttributes

Kind: property

```swift
static var userAttributes: [String: Any]?
```

Attributes of the current user, shown in the dashboard.

- Values must be JSON-representable: strings, numbers, booleans, `NSNull`, or arrays and dictionaries of those. An assignment with any other value is rejected and logged.
- Held while the SDK is disabled.

---

## Links

### generateLink

Kind: method

```swift
static func generateLink(
    title: String? = nil,
    subtitle: String? = nil,
    imageURL: String? = nil,
    data: [String: Any]? = nil,
    tags: [String]? = nil,
    customRedirects: CustomRedirects? = nil,
    showPreviewiOS: Bool? = nil,
    showPreviewAndroid: Bool? = nil,
    copyToClipboardiOS: Bool? = nil,
    copyToClipboardAndroid: Bool? = nil,
    trackingCampaign: String? = nil,
    trackingSource: String? = nil,
    trackingMedium: String? = nil,
    completion: @escaping GrovsURLClosure
)
```

Creates a smart link. See [Link Creation](https://docs.grovs.io/docs/sdk/ios/link-creation).

| Name                     | Type               | Description                                                                                                                                                                                                      |
| ------------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`                  | `String?`          | Link preview title.                                                                                                                                                                                              |
| `subtitle`               | `String?`          | Link preview subtitle.                                                                                                                                                                                           |
| `imageURL`               | `String?`          | Link preview image URL.                                                                                                                                                                                          |
| `data`                   | `[String: Any]?`   | Custom payload delivered on deep link open.                                                                                                                                                                      |
| `tags`                   | `[String]?`        | Tags for filtering and analytics.                                                                                                                                                                                |
| `customRedirects`        | `CustomRedirects?` | Per-platform [redirect overrides](#customredirects).                                                                                                                                                             |
| `showPreviewiOS`         | `Bool?`            | Show the preview page on iOS. `nil` uses the project default.                                                                                                                                                    |
| `showPreviewAndroid`     | `Bool?`            | Show the preview page on Android. `nil` uses the project default.                                                                                                                                                |
| `copyToClipboardiOS`     | `Bool?`            | Copy the link on the iOS preview page, for [clipboard-assisted deferred deep linking](https://docs.grovs.io/docs/sdk/ios/deep-linking#clipboard-assisted-deferred-deep-linking). `nil` uses the project default. |
| `copyToClipboardAndroid` | `Bool?`            | Same, for the Android preview page.                                                                                                                                                                              |
| `trackingCampaign`       | `String?`          | UTM campaign.                                                                                                                                                                                                    |
| `trackingSource`         | `String?`          | UTM source.                                                                                                                                                                                                      |
| `trackingMedium`         | `String?`          | UTM medium.                                                                                                                                                                                                      |
| `completion`             | `GrovsURLClosure`  | Gets the URL, or `nil` on failure.                                                                                                                                                                               |

### linkDetails

Kind: method

```swift
static func linkDetails(path: String, completion: @escaping GrovsLinkClosure)
```

Gets details for a link generated in the current environment, by path.

**Returns** through `completion`: the details, or `nil` if the request fails.

---

## Events

See [Events & Screen Tracking](https://docs.grovs.io/docs/sdk/ios/analytics).

### track

Kind: method · Since 3.0

```swift
static func track(_ name: String, properties: [String: Any]? = nil, tags: [String]? = nil)
```

Tracks a custom event.

- The name must be non-empty and not [reserved](https://docs.grovs.io/docs/sdk/ios/in-app-messages#reserved-event-names).
- Events can trigger in-app messages.
- Property rules and limits: [Tracking custom events](https://docs.grovs.io/docs/sdk/ios/analytics#tracking-custom-events).

### trackScreenView

Kind: method · Since 3.0

```swift
static func trackScreenView(_ screenName: String, properties: [String: Any]? = nil)
```

Tracks a screen view.

- `screen_name` is added to the properties.
- A repeat of the same name within one second is dropped.

### setGlobalTags

Kind: method · Since 3.0

```swift
static func setGlobalTags(_ tags: [String]?)
```

Sets tags attached to every later event. Pass `nil` to clear them.

### setScreenAliases

Kind: method · Since 3.0

```swift
static func setScreenAliases(_ aliases: [String: String])
```

Maps view controller class names to dashboard names, for example `["CheckoutViewController": "Checkout Page"]`.

### screenNameProvider

Kind: property · Since 3.0

```swift
static var screenNameProvider: ((UIViewController) -> GrovsScreenName)?
```

Optional resolver for automatically tracked screen names. Returns a [`GrovsScreenName`](#grovsscreenname).

- Consulted before the SDK's own rules.
- Called on the main thread. Works before `configure`.
- See [Custom resolver](https://docs.grovs.io/docs/sdk/ios/analytics#custom-resolver).

---

## Revenue

See [Revenue](https://docs.grovs.io/docs/sdk/ios/revenue).

### logInAppPurchase

Kind: method · Since 3.0

```swift
static func logInAppPurchase(transactionID: UInt64, completion: @escaping GrovsBoolCompletion)
```

Logs an App Store purchase by its StoreKit 2 transaction ID.

- Only the ID is sent. The backend resolves product, price and currency through the App Store Server API.
- An ID already logged on this device is skipped.
- The completion gets `true` once sent. Once configured and enabled, `false` means the purchase is kept and retried, so don't log it again.
- Getting the ID needs StoreKit 2 (iOS 15) in your code.

### logCustomPurchase

Kind: method · Since 3.0

```swift
static func logCustomPurchase(
    type: TransactionType,
    priceInCents: Int,
    currency: String,
    productID: String,
    startDate: Date? = nil,
    completion: @escaping GrovsBoolCompletion
)
```

Logs a purchase, cancellation or refund made outside Apple In-App Purchase.

| Name           | Type                  | Description                                                                                                                                               |
| -------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`         | `TransactionType`     | [`buy`, `cancel` or `refund`](#transactiontype).                                                                                                          |
| `priceInCents` | `Int`                 | Price in minor units.                                                                                                                                     |
| `currency`     | `String`              | ISO 4217 code.                                                                                                                                            |
| `productID`    | `String`              | Your product identifier.                                                                                                                                  |
| `startDate`    | `Date?`               | Defaults to now.                                                                                                                                          |
| `completion`   | `GrovsBoolCompletion` | `true` once sent. Once configured and enabled, `false` means the purchase is kept and retried, so don't log it again: a second call is a second purchase. |

---

## Push notifications

Setup: [Push Notifications](https://docs.grovs.io/docs/sdk/ios/push-notifications).

### pushToken

Kind: property

```swift
static var pushToken: String?
```

The APNs device token as a hex string, for push campaigns.

- Set it with [`setPushToken(deviceToken:)`](#setpushtoken) from the APNs `Data`.
- Held before `configure` and while the SDK is disabled.

### setPushToken

Kind: method · Since 3.1

```swift
static func setPushToken(deviceToken: Data)
```

Sets [`pushToken`](#pushtoken) from the token passed to `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`.

### handleNotificationResponse

Kind: method · Since 3.1

```swift
@discardableResult
static func handleNotificationResponse(_ response: UNNotificationResponse) -> Bool
```

Handles a tapped Grovs push. Call from `userNotificationCenter(_:didReceive:withCompletionHandler:)`.

**Returns** `true` when Grovs sent the notification, whatever the action.

- Reports a campaign open (`push_open`, once per device) and delivers the push to `grovsReceivedPayloadFromDeeplink`.
- The payload is `userInfo` without `aps`, merged over the link's payload if there is one. The push's keys win.
- Dismissals and custom actions aren't opens.
- Taps before `configure` are held until it runs.

### handleNotificationOpen

Kind: method · Since 3.1

```swift
@discardableResult
static func handleNotificationOpen(userInfo: [AnyHashable: Any]) -> Bool
```

Same as `handleNotificationResponse`, when you only have the payload. Always treated as a tap.

- Never call it from `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` or `userNotificationCenter(_:willPresent:withCompletionHandler:)`. Every delivery would count as an open.

### isGrovsNotification

Kind: method · Since 3.1

```swift
static func isGrovsNotification(userInfo: [AnyHashable: Any]) -> Bool
```

Checks whether Grovs sent the push as part of a campaign, without handling it.

**Returns** `true` when `grovs_campaign_id` is non-empty.

- Since 3.1, a push with only `grovs_notification_id` returns `false`.

---

## In-app messages

See [In-app messages](https://docs.grovs.io/docs/sdk/ios/in-app-messages). Every method here works before `configure` and keeps its effect across later `configure` calls.

### inAppDelegate

Kind: property · Since 3.1

```swift
static var inAppDelegate: GrovsInAppDelegate?
```

Receives in-app message events (shown, tapped, dismissed, answered) and can stop a message. See [`GrovsInAppDelegate`](#grovsinappdelegate).

- Held **weakly**. Read it on the main thread.
- Messages show without it. See [React to messages](https://docs.grovs.io/docs/sdk/ios/in-app-messages#react-to-messages).

### pauseInAppMessages

Kind: method · Since 3.1

```swift
static func pauseInAppMessages()
```

Holds in-app messages back, during a checkout or video, say. The newest highest priority one waits.

### resumeInAppMessages

Kind: method · Since 3.1

```swift
static func resumeInAppMessages()
```

Lets messages show again, starting with the one that waited, if it still qualifies.

### discardInAppMessages

Kind: method · Since 3.1

```swift
static func discardInAppMessages(dismissCurrent: Bool = false)
```

Drops the waiting message and stays paused. With `dismissCurrent: true`, also closes the message on screen.

### setInAppExcludedScreens

Kind: method · Since 3.1

```swift
static func setInAppExcludedScreens(_ screens: [String])
```

Sets screens where no message shows. Messages wait until the user leaves.

- Use tracked screen names, from `trackScreenView` or automatic tracking.
- Case is ignored.

### setInAppDebugLogging

Kind: method · Since 3.1

```swift
static func setInAppDebugLogging(_ enabled: Bool)
```

Logs, at info level, each campaign considered for an event and why it was skipped.

---

## Types

### GrovsDelegate

Kind: protocol

```swift
@objc public protocol GrovsDelegate: AnyObject {
    func grovsReceivedPayloadFromDeeplink(link: String?, payload: [String: Any]?, tracking: [String: Any]?)
    @objc optional func grovsDidEncounterError(_ error: GrovsError, message: String)
}
```

Receives deep link payloads and SDK errors. Set it with [`configure`](#configure) or [`delegate`](#delegate).

| Name       | Type             | Description                                            |
| ---------- | ---------------- | ------------------------------------------------------ |
| `link`     | `String?`        | The link that opened the app, or `nil` if unavailable. |
| `payload`  | `[String: Any]?` | The custom data attached to the link.                  |
| `tracking` | `[String: Any]?` | Attribution and campaign metadata for the link.        |

- Both methods run on the **main thread**.
- Since 3.1 the protocol is `@objc`, so Objective-C apps can adopt it.
- `grovsDidEncounterError` is an `@objc optional` method, not a Swift default implementation. A signature that doesn't match exactly compiles with only a "nearly matches" warning and is never called.

### GrovsError

Kind: enum

```swift
@objc public enum GrovsError: Int, CustomStringConvertible {
    case authenticationFailed = 1
    case networkRequestFailed = 2
    case eventDispatchFailed = 3
    case linkGenerationFailed = 4
    case inAppDisplayFailed = 5
}
```

Failure categories reported through `grovsDidEncounterError`. See [Handling SDK errors](https://docs.grovs.io/docs/sdk/ios/deep-linking#handling-sdk-errors).

- `inAppDisplayFailed` (new in 3.1): an in-app message couldn't be drawn. It gains no view and retries at its next trigger.
- An exhaustive `switch` needs a case for `inAppDisplayFailed`.
- `description` is the snake-case name, for example `"authentication_failed"`.

### GrovsInAppDelegate

Kind: protocol · Since 3.1

```swift
@objc public protocol GrovsInAppDelegate: AnyObject {
    @objc optional func grovsShouldShowInAppMessage(campaignID: String) -> Bool
    @objc optional func grovsDidShowInAppMessage(campaignID: String)
    @objc optional func grovsDidTapInAppMessage(campaignID: String, buttonID: String?, payload: [String: Any]?)
    @objc optional func grovsDidDismissInAppMessage(campaignID: String, reason: String)
    @objc optional func grovsDidAnswerInAppMessage(campaignID: String, value: Any, comment: String?)
}
```

Receives in-app message events. Every method is optional and runs on the **main thread**.

| Method                        | Called when                                                                                                                                                     |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `grovsShouldShowInAppMessage` | A message is about to show. Return `false` to stop it. It isn't counted as seen, and nothing else is shown for that event.                                      |
| `grovsDidShowInAppMessage`    | A message is on screen.                                                                                                                                         |
| `grovsDidTapInAppMessage`     | The user taps. `buttonID` is `nil` for a tap on the message itself. `payload` is set for a key-value action.                                                    |
| `grovsDidDismissInAppMessage` | A message closes. See the reasons below.                                                                                                                        |
| `grovsDidAnswerInAppMessage`  | An answered message has closed, in place of the dismissal. `value` is an `Int` for stars, the option's ID for a choice. `comment` is `nil` unless one was sent. |

| Dismiss `reason` | Meaning                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `close_button`   | The user tapped the close button.                                                                            |
| `backdrop`       | The user tapped outside the message.                                                                         |
| `swipe`          | The user swiped it away.                                                                                     |
| `app`            | Your app closed it, for example with `discardInAppMessages(dismissCurrent: true)`.                           |
| `orientation`    | The device turned to an orientation the campaign doesn't allow. Not counted as a dismissal in the dashboard. |

### GrovsScreenTracking

Kind: protocol · Since 3.0

```swift
public protocol GrovsScreenTracking {
    var grovsScreenName: String? { get }
}
```

Adopt on a `UIViewController` to name it for automatic screen tracking. Return `nil` to skip it.

### GrovsScreenName

Kind: enum · Since 3.0

```swift
public enum GrovsScreenName {
    case automatic
    case suppress
    case name(String)
}
```

Returned from [`screenNameProvider`](#screennameprovider).

| Value        | Effect                                 |
| ------------ | -------------------------------------- |
| `.automatic` | Falls through to the SDK's resolution. |
| `.suppress`  | Skips the controller.                  |
| `.name(_:)`  | Overrides the name.                    |

### LogLevel

Kind: enum · Since 3.0

```swift
public enum LogLevel: Int, Comparable {
    case info = 0
    case warn = 1
    case error = 2
}
```

Passed to [`setDebug`](#setdebug).

| Value    | Logs                                          |
| -------- | --------------------------------------------- |
| `.info`  | Everything, including informational messages. |
| `.warn`  | Warnings and errors.                          |
| `.error` | Errors only (default).                        |

### TransactionType

Kind: enum

```swift
public enum TransactionType: String, Codable {
    case buy
    case cancel
    case refund
}
```

Used with [`logCustomPurchase`](#logcustompurchase).

### CustomRedirects

Kind: type

```swift
public struct CustomRedirects: Codable {
    public var ios: CustomLinkRedirect?
    public var android: CustomLinkRedirect?
    public var desktop: CustomLinkRedirect?

    public init(ios: CustomLinkRedirect? = nil, android: CustomLinkRedirect? = nil, desktop: CustomLinkRedirect? = nil)
}
```

Per-platform redirect overrides for [`generateLink`](#generatelink).

### CustomLinkRedirect

Kind: type

```swift
public struct CustomLinkRedirect: Codable {
    public let link: String
    public var openAppIfInstalled: Bool

    public init(link: String, openAppIfInstalled: Bool = true)
}
```

One platform redirect. When `openAppIfInstalled` is `true`, the app opens if installed. Otherwise `link` is always used.

### Closure types

| Type                            | Signature                                                                                 |
| ------------------------------- | ----------------------------------------------------------------------------------------- |
| `GrovsBoolCompletion`           | `(Bool) -> Void`                                                                          |
| `GrovsURLClosure`               | `(URL?) -> Void`                                                                          |
| `GrovsPayloadClosure`           | `([String: Any]?) -> Void`                                                                |
| `GrovsPayloadsClosure`          | `([[String: Any]]?) -> Void`                                                              |
| `GrovsLinkClosure`              | `([String: Any]?) -> Void`                                                                |
| `GrovsIntClosure`               | `(Int?) -> Void`                                                                          |
| `GrovsEmptyClosure`             | `() -> Void`                                                                              |
| `GrovsDeviceDataClosureClosure` | `(Bool, [String: Any]?, String?, [String: Any]?) -> Void`. Not used by any public method. |

---

## Extensions

### GrovsNotificationServiceExtension

Kind: class · Since 3.1

```swift
open class GrovsNotificationServiceExtension: UNNotificationServiceExtension {
    public var session: URLSession  // defaults to .shared
}
```

Shows campaign images in pushes. Make it the superclass of your Notification Service Extension's principal class.

- In the `GrovsNotificationService` module (SPM product or pod). Safe to use from app extensions.
- Downloads the image and attaches it if it's JPEG, PNG or GIF.
- The push is delivered once, without the image if the download fails or time runs out.

### GrovsNotificationContent

Kind: enum · Since 3.1

```swift
public enum GrovsNotificationContent {
    public static let imageKey: String  // "grovs_image"

    @discardableResult
    public static func attachImage(to content: UNNotificationContent, session: URLSession = .shared,
                                   completion: @escaping (UNNotificationContent) -> Void) -> URLSessionTask?

    @discardableResult
    public static func attachImage(to request: UNNotificationRequest, session: URLSession = .shared,
                                   completion: @escaping (UNNotificationContent) -> Void) -> URLSessionTask?
}
```

Adds the campaign image to an existing extension's content, keeping your changes. In the `GrovsNotificationService` module.

**Returns** the download task, or `nil` when there's nothing to download.

- `completion` runs once on a background queue, or right away when there's no image.
- It gets the content unchanged when there's no image or it can't be shown.
- Cancel the returned task in `serviceExtensionTimeWillExpire()`. The completion then runs without the image.

### grovsScreen

Kind: method · Since 3.0

```swift
func grovsScreen(_ screenName: String, properties: [String: Any]? = nil) -> some View
func grovsScreen(properties: [String: Any]? = nil, fileID: String = #fileID) -> some View
```

SwiftUI modifier. Tracks a screen view when the view appears.

- Suppresses automatic tracking for its hosting controller, so the screen is never counted twice.
- The unnamed form uses the **file name**: `CheckoutView.swift` becomes `"Checkout"`.

### trackScreenView (modifier)

Kind: method

```swift
func trackScreenView(_ screenName: String, properties: [String: Any]? = nil) -> some View
```

SwiftUI modifier. Same as `grovsScreen(_:properties:)`.

---

## Deprecated

The messages inbox was removed in 3.1. These methods remain so existing code compiles, and show nothing. Use [in-app messages](#in-app-messages).

### displayMessagesViewController

Kind: method · Deprecated in 3.1

```swift
static func displayMessagesViewController(completion: GrovsEmptyClosure?)
static func displayMessagesViewController(completion: GrovsEmptyClosure?, onFailure: GrovsEmptyClosure?)
```

Shows nothing. Calls `completion` once on the main thread and never calls `onFailure`.

### numberOfUnreadMessages

Kind: method · Deprecated in 3.1

```swift
static func numberOfUnreadMessages(completion: @escaping GrovsIntClosure)
```

Always yields `0`, on the main thread.
