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

# Expo and React Native

> Add Rollup to an Expo or React Native app with the expo-rollup module and its config plugin.

`expo-rollup` brings the Rollup SDK to Expo and React Native apps. It wraps the native iOS SDK: indexing and search run in Swift on the iPhone, and the module passes calls and results to JavaScript. Its config plugin sets up your iOS project for you.

<Warning>
  Rollup runs on iPhone only, with iOS 26 or later. In the iOS Simulator, `configure` rejects with `simulatorUnsupported`. On Android and the web, every call rejects with `unsupportedPlatform`, so a cross-platform app still builds and runs there.
</Warning>

## Before you start

* An approved app in the [developer dashboard](https://www.tryrollup.com/developers), with your Apple Team ID, your app's bundle ID and its publishable key (`rk_live_...`). The [Quickstart](/quickstart#get-access) explains how to get one.
* A [development build](https://docs.expo.dev/develop/development-builds/introduction/). Expo Go does not include this module.
* A real iPhone with iOS 26 or later. Development builds on a device work: they attest in Apple's development App Attest environment, which Rollup accepts.

## Install

<Steps>
  <Step title="Add the package">
    ```sh theme={null}
    npx expo install expo-rollup
    ```
  </Step>

  <Step title="Add the config plugin">
    In `app.json` or `app.config.js`, add the plugin with the text iOS shows when it asks for Photos access:

    ```json app.json theme={null}
    {
      "expo": {
        "ios": { "bundleIdentifier": "com.example.app" },
        "plugins": [
          ["expo-rollup", { "photosPermission": "Your photos help personalize the app. They stay on your iPhone." }]
        ]
      }
    }
    ```

    The bundle ID must be the one you registered in the dashboard.
  </Step>

  <Step title="Build for your iPhone">
    ```sh theme={null}
    npx expo prebuild --platform ios
    npx expo run:ios --device
    ```

    Or build with [EAS Build](https://docs.expo.dev/build/introduction/).
  </Step>
</Steps>

The config plugin makes every change the [Quickstart](/quickstart#configure-your-app-target) describes for a native app:

* `NSPhotoLibraryUsageDescription`, from the `photosPermission` option.
* The background task identifiers `<bundle ID>.rollup.indexing.*` and `<bundle ID>.rollup.update` in `BGTaskSchedulerPermittedIdentifiers`, and `processing` in `UIBackgroundModes`.
* The App Attest entitlement.
* An iOS deployment target of 26.0.
* The Rollup pod in your Podfile, from Rollup's podspec. Rollup is not on CocoaPods trunk.

The module also registers Rollup's background task when the app launches, so you don't need to change your app delegate.

### Plugin options

| Option | Default | Meaning |
| - | - | - |
| `photosPermission` | A short default text | The Photos permission text. Say why your app reads the person's photos. |
| `appAttestEnvironment` | `"production"` | `"development"` or `"production"`. Rollup accepts both. |
| `rollupPodspec` | The podspec of the iOS SDK version this module was built for | Where CocoaPods gets the Rollup pod. |

### Bare React Native

Set up Expo Modules first (`npx install-expo-modules@latest`), then install the package with `npm install expo-rollup`. Without a config plugin, make its changes yourself: the Info.plist entries and App Attest capability from the [Quickstart](/quickstart#configure-your-app-target), the iOS 26 deployment target, and this line in your Podfile's app target, after `use_expo_modules!`:

```ruby Podfile theme={null}
pod 'Rollup', :podspec => 'https://raw.githubusercontent.com/KINT-AI/rollup-ios-sdk/0.1.0/Rollup.podspec'
```

## Use

```tsx theme={null}
import * as Rollup from 'expo-rollup';

// Once, when your app starts. Options you leave out keep the SDK's defaults (the last three months, growing).
await Rollup.configure('rk_live_...', {
  window: Rollup.indexWindow.growing(Rollup.span.months(3)),
});

// From a screen where the person has just chosen to turn the feature on. A grant starts the first index.
const access = await Rollup.requestPhotoAccess();
```

In a component, `useIndexingState` keeps the indexing state current, and `isSearchable` tells you when search, query and profile have an index to read:

```tsx theme={null}
function PhotoSearch() {
  const state = Rollup.useIndexingState();
  if (!Rollup.isSearchable(state)) return <Text>Indexing your photos</Text>;
  // ...
}
```

Then search the index on the iPhone, ask about the person's life, or fill a profile:

```tsx theme={null}
const results = await Rollup.search('ramen in Tokyo last spring');

const answer = await Rollup.query('Where do I eat most?');

const profile = await Rollup.profile({
  type: 'object',
  properties: {
    favorite_cuisine: { type: 'string', description: 'The cuisine they eat most often' },
  },
});
```

Each call works as in the Swift SDK. See [Search](/guides/search), [Query](/guides/query), [Profile](/guides/profile) and [Index options](/guides/index-options).

### Showing photos

Results identify photos by their PhotoKit local identifier: `result.id` in search results, `answer.assetIdentifiers` for an answer, and `profile.evidence` for each filled field. Show them with [`expo-media-library`](https://docs.expo.dev/versions/latest/sdk/media-library/), or in React Native's `Image` with a `ph://` URI:

```tsx theme={null}
<Image source={{ uri: `ph://${result.id}` }} style={{ width: 120, height: 120 }} />
```

### Errors

Calls reject with a `RollupError`. Its `code` is one of the Swift SDK's [error codes](/reference/rollup-error), or one of two that come from the module: `unsupportedPlatform` (not an iPhone) and `invalidOptions` (index options the SDK can't read). Errors from Rollup's server also carry `serverCode`, `retryable` and `retryAfter`.

```ts theme={null}
try {
  await Rollup.query('Where do I eat most?');
} catch (error) {
  if (Rollup.isRollupError(error, 'quotaExceeded')) {
    // The app's monthly budget is used up until the date in error.message.
  }
}
```

## API

| JavaScript | Swift |
| - | - |
| `configure(key, options?)` | `Rollup.configure(publishableKey:indexing:)` |
| `requestPhotoAccess()` | `Rollup.shared.requestPhotoAccess()` |
| `getIndexingState()`, `addIndexingStateListener(listener)`, `useIndexingState()` | `Rollup.shared.indexing.state`, `.states` |
| `getIndexOptions()` | `Rollup.shared.indexing.options` |
| `startIndexing()` | `Rollup.shared.indexing.start()` |
| `search(text)` | `Rollup.shared.search(_:)` |
| `query(question)` | `Rollup.shared.query(_:)` |
| `profile(schema)` | `Rollup.shared.profile(schema:)` |
| `reset()` | `Rollup.shared.reset()` |
| `isAvailable`, `sdkVersion` | `Rollup.sdkVersion` |

Values have the same fields as the [Swift types](/reference/overview), with dates as ISO 8601 strings and enums as objects tagged by `type` (for example `{ type: 'ready', photos: 1200, updatedAt: '...' }`). The package ships TypeScript types for all of them, and [JavaScript API](/platforms/javascript-api) lists every function and type.

`queryUpdates`, which streams an answer as it is written, is not in the module yet. Use `query` for the finished answer.

## Versions

| expo-rollup | Rollup iOS SDK |
| - | - |
| 0.1.0 | 0.1.0 |

## Privacy and App Review

`search` runs on the iPhone. `query` and `profile` send text the index derived from the person's photos to Rollup's server and its model provider: dates, places, labels and text read in photos. Before your app calls them, tell the person and get their permission. App Store Review Guideline 5.1.2(i) requires this for personal data shared with third-party AI. See [Privacy](/guides/privacy).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.