# React Native SDK 3.0

> What changes when you move the Grovs React Native wrapper from 1.x to 3.0.

Source: https://docs.grovs.io/docs/upgrade-guides/react-native-3-0

`react-native-grovs-wrapper` 3.0.0 moves to the Grovs iOS SDK 3.0 and Android SDK 3.0.0. It inherits their automatic screen tracking, consent gating and clipboard-assisted deferred deep linking.

One JavaScript signature changes: `logInAppPurchase` takes an object instead of a string. Everything else from 1.x still compiles, including the positional `generateLink`. Most apps bump the packages, update the two native `configure` calls, and fix their `logInAppPurchase` calls.

Requirements are unchanged: React Native 0.70 or later, iOS 13.0, Android 5.0 (API 21).

> **Note:** There is no 2.x. The wrapper goes from 1.x to 3.0.0 to share a major version with the native SDKs it wraps.

## Checklist

1. [Bump the packages](#1-bump-the-packages): the wrapper, `io.grovs:Grovs` in your app's `build.gradle`, and the iOS pods.
2. [Update the native `configure` calls](#2-update-the-native-configure-calls): turn native screen tracking off and pass the stored consent value.
3. [Update `logInAppPurchase` calls](#3-update-loginapppurchase-calls): the argument is now an object.
4. [Review your consent gating](#4-review-your-consent-gating): `setSDK` is persisted, and three methods reject with `SDK_DISABLED` while disabled.
5. [Check the other behavior changes](#5-check-the-other-behavior-changes): the clipboard read on first launch, `startDate` on Android, and your store privacy declarations.

## 1. Bump the packages

```bash
npm install react-native-grovs-wrapper@^3.0.0
# or
yarn add react-native-grovs-wrapper@^3.0.0
```

**Android.** Your app declares its own Grovs Android SDK dependency, because `MainApplication` and `MainActivity` call it directly. Move it from 1.1.1 to 3.0.0 in `android/app/build.gradle`:

```groovy
dependencies {
    implementation 'io.grovs:Grovs:3.0.0'
}
```

The wrapper is built against this exact coordinate, so keep the two in step. In 1.x the wrapper's `build.gradle` hard-coded `io.grovs:Grovs:1.1.1`. It now reads the coordinate from its `gradle.properties`.

**iOS.** The podspec dependency moves from `Grovs ~> 2.3` to `Grovs ~> 3.0`. Run:

```bash
cd ios && pod install
```

If `pod install` keeps a 2.x `Grovs` pinned in your `Podfile.lock`, run `pod update Grovs`.

**Expo.** The config plugin injects different `configure` calls in 3.0, but it never edits a file that already contains `Grovs.configure`. Regenerate the native projects:

```bash
npx expo prebuild --clean
```

Save any manual native edits first. The plugin also gained an optional `clipboardDomains` option; see [Expo integration](https://docs.grovs.io/docs/sdk/react-native/quick-start#expo-integration).

## 2. Update the native `configure` calls

> **Warning:** Skipping this step changes behavior without any code edit on your side: the native SDKs' automatic screen tracking is on by default in 3.0.

**Turn native screen tracking off.** The native trackers only see the single Activity or view controller that hosts React Native, so leaving them on reports one meaningless screen. Pass `autoTrackScreenViews: false` and track screens from JavaScript with [`startScreenTracking`](https://docs.grovs.io/docs/sdk/react-native/analytics#automatic-screen-tracking-with-react-navigation) or `trackScreenView`.

**Pass the stored consent value.** `setSDK` now persists its value, and the wrapper exposes it to native code as `GrovsConsent.isEnabled(context)` on Android and `GrovsWrapperSwift.isSDKEnabled()` on iOS. Pass it as `enabled` so a user who opted out stays opted out from the first frame of the next launch.

```kotlin
// 1.x
Grovs.configure(this, "your-api-key", useTestEnvironment = false)

// 3.0
import com.grovswrapper.GrovsConsent

Grovs.configure(
    this, "your-api-key", useTestEnvironment = false, baseURL = null,
    autoTrackScreenViews = false, clipboardDomains = null,
    enabled = GrovsConsent.isEnabled(this)
)
```

```swift
// 1.x
Grovs.configure(APIKey: "your-api-key", useTestEnvironment: false, delegate: nil)

// 3.0
import react_native_grovs_wrapper

Grovs.configure(
    APIKey: "your-api-key", useTestEnvironment: false,
    autoTrackScreenViews: false, clipboardDomains: nil,
    enabled: GrovsWrapperSwift.isSDKEnabled(), delegate: nil
)
```

The Android `configure` has no defaults after `baseURL`, so every parameter must be passed. Delegate handling is unchanged: keep `delegate: nil` (or `GrovsWrapperSwift.shared`, as the Expo plugin injects) and keep returning `super.application(...)`. The `MainActivity` forwarding and the intent filters are unchanged too. See the [Quick Start](https://docs.grovs.io/docs/sdk/react-native/quick-start) for the complete files.

## 3. Update `logInAppPurchase` calls

In 1.x the method took one string, and on Android that string was passed to the native SDK as the purchase JSON:

```typescript
// 1.x
import { Platform } from 'react-native';

await Grovs.logInAppPurchase(Platform.OS === 'ios' ? transaction.id : purchase.originalJson);
```

In 3.0 it takes an `InAppPurchase` object with one field per platform:

```typescript
// 3.0
import { Platform } from 'react-native';
import Grovs from 'react-native-grovs-wrapper';

await Grovs.logInAppPurchase(
    Platform.OS === 'ios'
        ? { transactionId: transaction.id }
        : { originalJson: purchase.originalJson }
);
```

The wrapper now validates before calling native: it rejects if `transactionId` is missing or not numeric on iOS, or if `originalJson` is missing on Android. A call that still passes a string fails to type-check and rejects at runtime. See [Revenue](https://docs.grovs.io/docs/sdk/react-native/revenue#platform-store-purchases).

## 4. Review your consent gating

**`setSDK(false)` now stops everything natively.** In the native SDKs the 1.x wrapper used (Android 1.1.1, iOS 2.x) it blocked link generation and deep link resolution but left lifecycle event collection running. In 3.0, while disabled, the native SDK does not authenticate, resolve the device, read the clipboard, or send lifecycle events, custom events, screen views, or purchases. Identifier, attributes, push token, and screen aliases are held and synced when you enable it again.

**The value is persisted by the wrapper.** `setSDK` writes the flag to `SharedPreferences` / `UserDefaults`, and the `enabled` parameter from step 2 reads it at the next launch. Without step 2, each launch starts enabled until your JavaScript calls `setSDK(false)`.

**Three methods reject while disabled.** `generateLink`, `displayMessages`, and `numberOfUnreadMessages` reject with an error whose `code` is `'SDK_DISABLED'` and whose message ends with `Grovs SDK is disabled. Call setSDK(true) first.` In 1.x the wrapper rewrapped every native error as a plain `Error` with only a message; the `code` property is new. `track`, `trackScreenView`, and the purchase methods do not reject; the native SDK sends nothing while disabled.

**Enabling replays the launch link on Android only.** `setSDK(true)` forwards `onStart` for the current Activity again, so a link the app was opened with while disabled is delivered. On iOS it is not replayed.

See [Privacy & Consent](https://docs.grovs.io/docs/sdk/react-native/privacy).

## 5. Check the other behavior changes

**Clipboard-assisted deferred deep linking is on.** On the first launch after install, when fingerprint matching found nothing and the backend reports recent clicks on copy-to-clipboard links for your project, the native SDK reads the clipboard to attribute the install and delivers the match through `onDeeplinkReceived`. iOS may show its paste notice and Android 12 and later show the "pasted from your clipboard" toast once. If you never enable copy-to-clipboard for your project or its links, the clipboard is never read. See [Clipboard-assisted deferred deep linking](https://docs.grovs.io/docs/sdk/react-native/deep-linking#clipboard-assisted-deferred-deep-linking) and [Copy to Clipboard](https://docs.grovs.io/docs/links/copy-to-clipboard).

**`logCustomPurchase` honors `startDate` on Android.** In 1.x the Android bridge always used the current time; 3.0 parses the ISO 8601 string and falls back to now only if it cannot be parsed.

**Events are batched and held during install attribution.** These are native 3.0 behaviors; see the [Android](https://docs.grovs.io/docs/upgrade-guides/android-3-0#5-check-the-other-behavior-changes) and [iOS](https://docs.grovs.io/docs/upgrade-guides/ios-3-0) upgrade guides.

**Update your store privacy declarations.** Screen views and custom events are new categories of collection. Check your Google Play Data safety form and App Store privacy details against [what the SDKs collect](https://docs.grovs.io/docs/sdk/react-native/privacy#what-the-sdk-collects).

## What's new in 3.0

Nothing here is required.

- **[Custom events and screen tracking](https://docs.grovs.io/docs/sdk/react-native/analytics)**: `track`, `trackScreenView`, `startScreenTracking` for React Navigation, `setGlobalTags`, and `setScreenAliases`.
- **[Consent gating](https://docs.grovs.io/docs/sdk/react-native/privacy)**: a persisted `setSDK`, the `enabled` wiring for native `configure`, and the `SDK_DISABLED` error code.
- **[Clipboard-assisted deferred deep linking](https://docs.grovs.io/docs/sdk/react-native/deep-linking#clipboard-assisted-deferred-deep-linking)**: deterministic install attribution on first launch, with `clipboardDomains` in the Expo plugin and the native `configure` calls for custom link domains.
- **[`generateLink(options)`](https://docs.grovs.io/docs/sdk/react-native/link-creation)**: a `GenerateLinkOptions` object instead of eleven positional arguments, plus per-link `copyToClipboardIos` and `copyToClipboardAndroid` flags. The positional form still works and accepts the two flags at the end.
- **New types**: `GenerateLinkOptions`, `InAppPurchase`, and `NavigationContainerRefLike` are exported.

## Verifying the upgrade

Turn on info logging and watch the native logs (Logcat or Console.app, prefixed `GROVS`) and the Metro console (prefixed `GROVS JS`):

```typescript
Grovs.setDebug('info');
```

Then check that deep links still reach your `onDeeplinkReceived` listener, that `generateLink` resolves, and, if you added `startScreenTracking`, that screen views appear in the dashboard under your route names. Use [`setScreenAliases`](https://docs.grovs.io/docs/sdk/react-native/analytics#screen-name-aliases) to rename any you want to read differently.

## See also

- [Quick Start](https://docs.grovs.io/docs/sdk/react-native/quick-start): full native configuration
- [API Reference](https://docs.grovs.io/docs/sdk/react-native/api-reference): every method and type
- [SDK Releases](https://docs.grovs.io/docs/sdk/releases): where release notes are published
