# Privacy & consent

> Gate the Grovs React Native SDK on user consent, and see what it collects.

Source: https://docs.grovs.io/docs/sdk/react-native/privacy

## Gating collection on consent

Call `setSDK(false)` when the user declines analytics and `setSDK(true)` when they accept:

```typescript
import Grovs from 'react-native-grovs-wrapper';

async function applyConsent(granted: boolean) {
  await consentStore.save(granted);
  Grovs.setSDK(granted);
}
```

The native SDKs don't persist this flag, so the wrapper does. `setSDK` stores it (`SharedPreferences` on Android, `UserDefaults` on iOS) and forwards it to the native SDK. Before anything is stored, it's `true`.

### Start the next launch disabled

The native SDK is configured in `MainApplication` and `AppDelegate`, before any JavaScript runs, and `configure` starts collecting. A JavaScript `setSDK(false)` is too late for that launch unless native `configure` reads the stored value. Pass it as `enabled`, as the [Quick Start](https://docs.grovs.io/docs/sdk/react-native/quick-start) and the Expo config plugin do:

```kotlin
import com.grovswrapper.GrovsConsent

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

```swift
import react_native_grovs_wrapper

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

A user who declined then stays opted out from the first frame of the next cold start. Without it, every launch starts enabled until JavaScript calls `setSDK(false)`.

> **Warning:** `GrovsConsent.isEnabled` and `GrovsWrapperSwift.isSDKEnabled()` return `true` until `setSDK` is first called, so the first launch starts enabled. To opt out before the user answers, call `Grovs.setSDK(false)` as early as possible in JavaScript on that launch. Collection stops there and the next cold start begins disabled.

## While the SDK is disabled

The wrapper checks the stored flag before calling native for backend features:

- `generateLink`, `linkDetails`, `displayMessages` and `numberOfUnreadMessages` reject with `code` `'SDK_DISABLED'` and a message ending `Grovs SDK is disabled. Call setSDK(true) first.`
- `track`, `trackScreenView`, `logInAppPurchase` and `logCustomPurchase` are forwarded; the native SDK sends nothing while disabled.
- `setIdentifier`, `setAttributes`, `setPushToken` and `setScreenAliases` are forwarded; the native SDK holds the latest values and syncs them once enabled.
- Deep links aren't resolved, so `onDeeplinkReceived` doesn't fire. Push taps deliver nothing and no opens are reported.
- In-app messages don't show.

```typescript
try {
  const link = await Grovs.generateLink({ title: 'Invite' });
} catch (error) {
  if ((error as { code?: string }).code === 'SDK_DISABLED') {
    // ask for consent, then Grovs.setSDK(true)
  }
}
```

Natively, no authentication, device lookup, clipboard read, events, screen views or purchases leave the device, and outstanding requests are cancelled. Full lists: [Android](https://docs.grovs.io/docs/sdk/android/privacy#while-the-sdk-is-disabled) and [iOS](https://docs.grovs.io/docs/sdk/ios/privacy#while-the-sdk-is-disabled).

### Re-enabling

`setSDK(true)` stores the value and enables the native SDK, which authenticates if needed and sends everything held. On Android the wrapper re-forwards `onStart` for the current Activity, so a launch link from while disabled reaches your listener. On iOS it isn't replayed.

## What the SDK collects

The wrapper collects nothing itself: it stores the consent flag locally and forwards your calls. What leaves the device, including the clipboard read for [clipboard-assisted deferred deep linking](https://docs.grovs.io/docs/sdk/react-native/deep-linking#clipboard-assisted-deferred-deep-linking), is per platform:

- [What the Android SDK collects](https://docs.grovs.io/docs/sdk/android/privacy#what-the-sdk-collects), and the [Google Play data safety](https://docs.grovs.io/docs/sdk/android/privacy#google-play-data-safety) categories to declare
- [What the iOS SDK collects](https://docs.grovs.io/docs/sdk/ios/privacy#what-the-sdk-collects), the bundled [privacy manifest](https://docs.grovs.io/docs/sdk/ios/privacy#privacy-manifest), and the [App Store privacy details](https://docs.grovs.io/docs/sdk/ios/privacy#app-store-privacy-details) to declare

Anything in `setAttributes` or event properties (an email or name, say) is data you collect too and needs its own declaration.

## Logs

`setDebug('info')` turns on verbose native logging; `setDebug('error')` restores the default. Native logs go through Logcat and `os_log` (see [Android](https://docs.grovs.io/docs/sdk/android/privacy#logs) and [iOS](https://docs.grovs.io/docs/sdk/ios/privacy#logs)). The wrapper's JavaScript logs (which bridge it uses, listener registration) go to the Metro console prefixed `🔗GROVS JS`.
