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

# Send click and conversion events with InstantSearch iOS

> Unlock Algolia's most powerful features by sending click and conversion events from your InstantSearch iOS app.

export const UserToken = () => <Tooltip tip="A user token is a pseudonymous ID that represents an individual user across Algolia searches and events. It links queries, clicks, and conversions to a user profile, enabling user-level analytics, personalization, and recommendations." cta="User token" href=" /doc/guides/sending-events/concepts/usertoken">
    user token
  </Tooltip>;

export const FlavorSwitcher = ({current, baseHref = "", options = [], label = "InstantSearch framework"}) => {
  if (options.length === 0) {
    return <div className="not-prose" role="alert" style={{
      margin: "0.25rem 0 1.5rem",
      padding: "0.75rem",
      border: "1px solid #f59e0b",
      borderRadius: "0.625rem",
      color: "inherit",
      fontSize: "0.875rem"
    }}>
        FlavorSwitcher requires at least one option.
      </div>;
  }
  const selected = options.find(option => option.value === current) ?? options[0];
  return <div className="not-prose mint-flavor-switcher">
      <style>{`
        .mint-flavor-switcher {
          --mfs-bg: #ffffff;
          --mfs-bg-hover: #f4f4f5;
          --mfs-bg-current: #eef2ff;
          --mfs-border: #d4d4d8;
          --mfs-fg: #18181b;
          --mfs-muted: #71717a;
          --mfs-accent: #4f46e5;
          position: relative;
          width: min(100%, 19rem);
          margin: 0.25rem 0 1.5rem;
          color: var(--mfs-fg);
          font-size: 0.875rem;
          line-height: 1.25rem;
        }

        .dark .mint-flavor-switcher {
          --mfs-bg: #18181b;
          --mfs-bg-hover: #27272a;
          --mfs-bg-current: #272747;
          --mfs-border: #3f3f46;
          --mfs-fg: #fafafa;
          --mfs-muted: #a1a1aa;
          --mfs-accent: #a5b4fc;
        }

        .mint-flavor-switcher details {
          position: relative;
        }

        .mint-flavor-switcher summary {
          display: flex;
          min-height: 2.75rem;
          box-sizing: border-box;
          align-items: center;
          justify-content: space-between;
          gap: 0.75rem;
          padding: 0.625rem 0.75rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          color: var(--mfs-fg);
          cursor: pointer;
          font-weight: 600;
          list-style: none;
          transition: border-color 150ms ease, box-shadow 150ms ease;
        }

        .mint-flavor-switcher summary::-webkit-details-marker {
          display: none;
        }

        .mint-flavor-switcher summary:hover {
          border-color: var(--mfs-accent);
        }

        .mint-flavor-switcher summary:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: 2px;
        }

        .mint-flavor-switcher__label {
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .mint-flavor-switcher__chevron {
          flex: none;
          transition: transform 150ms ease;
        }

        .mint-flavor-switcher details[open] .mint-flavor-switcher__chevron {
          transform: rotate(180deg);
        }

        .mint-flavor-switcher__menu {
          position: absolute;
          z-index: 50;
          top: calc(100% + 0.375rem);
          left: 0;
          width: 100%;
          box-sizing: border-box;
          margin: 0;
          padding: 0.375rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          box-shadow: 0 12px 30px rgb(0 0 0 / 16%);
          list-style: none;
        }

        .mint-flavor-switcher__menu li {
          margin: 0;
          padding: 0;
        }

        .mint-flavor-switcher__option {
          display: grid;
          gap: 0.125rem;
          padding: 0.625rem 0.75rem;
          border-radius: 0.4rem;
          color: var(--mfs-fg);
          text-decoration: none;
        }

        .mint-flavor-switcher__option:hover {
          background: var(--mfs-bg-hover);
        }

        .mint-flavor-switcher__option:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: -2px;
        }

        .mint-flavor-switcher__option[aria-current="page"] {
          background: var(--mfs-bg-current);
          color: var(--mfs-accent);
        }

        .mint-flavor-switcher__name {
          font-weight: 600;
        }

        .mint-flavor-switcher__description {
          color: var(--mfs-muted);
          font-size: 0.8125rem;
        }

        @media (prefers-reduced-motion: reduce) {
          .mint-flavor-switcher summary,
          .mint-flavor-switcher__chevron {
            transition: none;
          }
        }
      `}</style>

      <details>
        <summary aria-label={`${label}: ${selected.label}`}>
          <span className="mint-flavor-switcher__label">{selected.label}</span>
          <svg className="mint-flavor-switcher__chevron" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="m6 9 6 6 6-6" />
          </svg>
        </summary>

        <ul className="mint-flavor-switcher__menu" aria-label={label}>
          {options.map(option => {
    const isCurrent = option.value === selected.value;
    const href = option.href ?? `${baseHref.replace(/\/$/, "")}/${encodeURIComponent(option.value)}`;
    return <li key={option.value}>
                <a className="mint-flavor-switcher__option" href={href} aria-current={isCurrent ? "page" : undefined}>
                  <span className="mint-flavor-switcher__name">
                    {option.label}
                  </span>
                  {option.description ? <span className="mint-flavor-switcher__description">
                      {option.description}
                    </span> : null}
                </a>
              </li>;
  })}
        </ul>
      </details>
    </div>;
};

<div className="mint-flavor-switcher-slot not-prose">
  <FlavorSwitcher
    current="ios"
    baseHref="/doc/guides/building-search-ui/events"
    options={[
{ value: "js", label: "JavaScript", description: "InstantSearch.js" },
{ value: "react", label: "React", description: "React InstantSearch" },
{ value: "vue", label: "Vue", description: "Vue InstantSearch" },
{ value: "ios", label: "iOS", description: "InstantSearch iOS" },
{ value: "android", label: "Android", description: "InstantSearch Android" },
{ value: "flutter", label: "Flutter", description: "Algolia for Flutter" },
]}
  />
</div>

<Info>
  Starting May 1, 2024,
  Apple requires all iOS apps to include a privacy manifest.
  For more information, see [Privacy manifest](/doc/guides/building-search-ui/resources/privacy-manifest/ios/).
</Info>

Events are actions that users take on your app or website.
They unlock powerful features, such as recommendations,
personalization, smarter search results,
and analytics that help you optimize your user experience.
For more information, see [Choose how to send events](/doc/guides/sending-events/getting-started).

To send events from your InstantSearch iOS app, follow these steps:

1. Add the `insights` client.
2. Add [click events](#send-click-events-from-hits-widgets) when users click search results.
3. [Track conversions](#send-click-conversion-and-view-events) that start in your InstantSearch app.

## Add the InstantSearch Insights library

Add the library as a dependency using Swift Package Manager or CocoaPods, and add `import InstantSearchInsights` to your source files.

### Swift Package Manager

To use Swift Package Manager, open your project with Xcode.
Go to **File > Add Packages** and enter the URL of the InstantSearch iOS library (`https://github.com/algolia/instantsearch-ios`).
In the **Choose Package Products for instantsearch-ios** dialog,
include `InstantSearchInsights`

If you're a framework author and use InstantSearch Insights as a dependency, update your `Package.swift` file:

```swift Swift icon=code theme={"system"}
let package = Package(
    dependencies: [
        .package(name: "InstantSearch", url: "https://github.com/algolia/instantsearch-ios", from: "8.0.0")
    ]
)
```

Add `.product(name: "InstantSearchInsights", package: "InstantSearch")` to your target dependencies list.

### CocoaPods

If you use [CocoaPods](https://cocoapods.org/),
add the following line to your `Podfile`:

```ruby Ruby theme={"system"}
pod("InstantSearch/Insights", "~> 8.0")
```

Run `pod update` to download the dependencies.

## Add the Insights client

To add the Insights client, you'll need your Algolia application ID, (search) API key, and a <UserToken /> to identify your users.
You can find your application ID and API key in the [Algolia dashboard](https://dashboard.algolia.com/account/api-keys).

```swift Swift icon=code theme={"system"}
Insights.register(appId: "testApp", apiKey: "testKey", userToken: "testToken")
```

To send events without timestamps, and be automatically attributed on the server, set the `generateTimestamps` parameter to `false`:

```swift Swift icon=code theme={"system"}
Insights.register(appId: "testApp",
                  apiKey: "testKey",
                  userToken: "testToken",
                  generateTimestamps = false)
```

To specify the region you prefer to use, set the `region` parameter to `.de` or `.us`:

```swift Swift icon=code theme={"system"}
Insights.register(appId: "testApp",
                  apiKey: "testKey",
                  userToken: "testToken",
                  region: .de)
```

### Customize the events flush delay

By default, the client sends events to Algolia every 30 minutes.
You can adjust this delay by changing the `flushDelay` value (in seconds):

```swift Swift icon=code theme={"system"}
Insights.flushDelay = 60
```

### Set the Insights API region

By default, each time you send an event, Algolia geo-routes the API call so that the call targets the closest servers.
The analytics API supports two regions: United States and Germany. You can specify the region you prefer to use as follows.

```swift Swift icon=code theme={"system"}
Insights.region = .de
```

## Enable automatic view events

InstantSearch can automatically send view events when hits are returned from Algolia. To enable this feature, set `isAutoSendingHitsViewEvents` to `true` when you initialize [`HitsSearcher`](/doc/api-reference/widgets/instantsearch/ios#hitssearcher):

```swift Swift icon=code theme={"system"}
let searcher = try HitsSearcher(appID: "ALGOLIA_APPLICATION_ID",
                                apiKey: "ALGOLIA_SEARCH_API_KEY",
                                indexName: "indexName",
                                isAutoSendingHitsViewEvents: true)
```

## Send click events from hits widgets

To send click events when users select a search result in a [`Hits`](/doc/api-reference/widgets/hits/ios) widget,
use the following code:

```swift Swift icon=code theme={"system"}
let searcher = try HitsSearcher(appID: "ALGOLIA_APPLICATION_ID",
                                apiKey: "ALGOLIA_SEARCH_API_KEY",
                                indexName: "indexName")
let hitsInteractor = HitsInteractor<Hit<JSON>>()

// Establish a connection between the searcher and hits interactor
hitsInteractor.connectSearcher(searcher)

// Declare a HitsTracker which takes care of activating clickAnalytics
// in the Query and extracts queryID from a received result for
// the index in the question
let hitsTracker = HitsTracker(eventName: "didClickItem", searcher: searcher)

// Did click the 10th item in the list
let hit = hitsInteractor.hit(atIndex: 10)!
hitsTracker.trackClick(for: hit, position: 10)
```

If you're using multiple [`Hits`](/doc/api-reference/widgets/hits/ios) widgets, you must enable sending click events on each one separately.

Check your click events in the [**Events Debugger**](https://dashboard.algolia.com/events/debugger).

## Send click, conversion, and view events

Use the following methods to send click, conversion, or view events:

```swift Swift icon=code theme={"system"}
Insights.shared?.clickedAfterSearch(eventName: "click event",
                                    indexName: "indexName",
                                    objectID: "object id",
                                    position: 1,
                                    queryID: "query id")

Insights.shared?.convertedAfterSearch(eventName: "conversion event",
                                      indexName: "indexName",
                                      objectIDs: ["obj1", "obj2"],
                                      queryID: "query id")

Insights.shared?.viewed(eventName: "view event",
                        indexName: "indexName",
                        filters: ["brand:apple"])
```

Conversions often happen outside your search pages.
For example, the Order completed event for a successful purchase happens in the shopping cart.
To capture these conversions, [keep track of the query ID](/doc/guides/sending-events/guides/queryid) across your app.

When you add events, check them in the [**Events Debugger**](https://dashboard.algolia.com/events/debugger) in the Algolia dashboard.
For more information, see [Validate your events](/doc/guides/sending-events/guides/validate).

## Enable logging

You can also check if you've sent an event by enabling logging:

```swift Swift icon=code theme={"system"}
Insights.shared(appId: "ALGOLIA_APPLICATION_ID")?.isLoggingEnabled = true
```

After you've enabled it, check the output for success messages or errors.

```txt theme={"system"}
// Success
[Algolia Insights - appName] Sync succeded for EventsPackage(id: "37E9A093-8F86-4049-9937-23E99E4E4B33", events: [{
    eventName = "search result click";
    eventType = click;
    index = "my index";
    objectIDs =     (
        1234567
    );
    positions =     (
        3
    );
    queryID = 08a76asda34fl30b7d06b7aa19a9e0;
    timestamp = 1545069313405;
    userToken = "C1D1322E-8CBF-432F-9875-BE3B5AFDA498";
}], region: nil)

//Error
[Algolia Insights - appName] The objectID field is missing (Code: 422)
```

For the `positions` parameter of the [`clickedObjectIdsAfterSearch`](/doc/libraries/sdk/v1/methods/clicked-object-ids-after-search) method,
the first object in the list of search results has a value of 1 (not 0), the second has a value of 2.
