# React Native and Expo

One package, `apparent-expo`, for Expo (managed or prebuild) and bare React Native. No native code of its own.

## Install

Expo:

```sh
npx expo install apparent-expo @react-native-async-storage/async-storage expo-application expo-localization
```

Bare React Native:

```sh
npm install apparent-expo @react-native-async-storage/async-storage && npx pod-install
```

Only `react-native` and `@react-native-async-storage/async-storage` are required. `expo-application`, `expo-localization`, `react-native-device-info` and `expo-router` are used when the app has them.

## Start it

In the root layout (app/_layout.tsx) or App.tsx, once:

```ts
import { Apparent } from "apparent-expo";

Apparent.init({ apiUrl: "https://in.apparent.fyi", app: "<app id>", key: "<write key>" });
```

Add `disabled: __DEV__` if you don't want development builds counted at all. You usually don't need to: simulators and test phones are left out of the numbers by themselves.

## Screens

expo-router, in the root layout:

```ts
import { useApparentScreens } from "apparent-expo/router";

useApparentScreens();
```

React Navigation:

```tsx
import { apparentScreens } from "apparent-expo/navigation";
import { NavigationContainer, useNavigationContainerRef } from "@react-navigation/native";

export default function App() {
  const ref = useNavigationContainerRef();
  return <NavigationContainer ref={ref} {...apparentScreens(ref)}>{/* … */}</NavigationContainer>;
}
```

Any other navigator: `Apparent.screen("settings")`.

## Events and users

```ts
Apparent.track("paywall_view", { source: "onboarding" });
Apparent.setProperties({ plan: "free" });   // added to every later event
Apparent.identify(user.id);                 // after sign-in
Apparent.reset();                           // on sign-out
```

More in [Events and users](https://apparent.fyi/docs/events).

## Purchases

```ts
await Apparent.linkRevenueCat(Purchases);   // after Purchases.configure
```

See [RevenueCat](https://apparent.fyi/docs/revenuecat).

## Errors and opting out

- Crashes and unhandled promise rejections are caught by themselves (release builds).
- Errors you catch: `Apparent.captureError(error)`.
- Someone declines analytics: `Apparent.optOut()`, and `Apparent.optIn()` to start again. Remembered.
- Ask for consent first? Start with `optOutByDefault: true` and call `Apparent.optIn()` once they agree. Nothing is collected before that.
- Nothing showing up? In a debug build the SDK prints `Apparent: app id or key is wrong — events are not being saved` once when the app id or key doesn't match. Copy both again from Connect.
- Simulators and emulators say so themselves (`simulator` in each batch), so they're never counted, even in a release build.

## Links, notifications and your own filter

- **Links.** When a link opens the app, the SDK sends `deep_link_opened` with the link (secrets like `token=` or `code=` replaced by `[redacted]`) and its `utm_source`, `utm_medium` and `utm_campaign`. A campaign link that opens the app on its first day counts as where that person came from. Turn it off with `trackDeepLinks: false`.
- **Notifications.** With `expo-notifications` in the app, a tap sends `push_opened` with the `campaign` and `title` you put in the notification's data. Never the text people saw. Turn it off with `trackPushOpens: false`.
- **Your own filter.** `beforeSend` sees every event before it is saved. Return it changed, or `null` to drop it:

```ts
Apparent.init({
  apiUrl: "https://in.apparent.fyi", app: "<app id>", key: "<write key>",
  beforeSend: (event) => {
    if (event.name === "debug_tap") return null;
    const { email, ...props } = event.props ?? {};
    return { ...event, props };
  },
});
```

## Bare React Native: versions

Without `expo-application` or `react-native-device-info` the SDK can't read your app's version. Pass it, or app updates can't be noticed:

```ts
import { version } from "./package.json";

Apparent.init({ apiUrl: "https://in.apparent.fyi", app: "<app id>", key: "<write key>", appVersion: version });
```

## What it promises

- Every event is saved on the phone within a quarter second, and right away when the app goes to the background or crashes.
- Up to 1,000 events wait offline for a week. Nothing is sent twice by mistake: the server ignores repeats.
- One request at a time, 10 seconds each, retries that wait longer each time.

Automatic events: `first_open`, `app_open`, `app_updated`, `app_background`, `screen_view`, `error`, `deep_link_opened`, `push_opened` (with `expo-notifications`), and `install_source` on Android.
