Skip to content

Latest commit

 

History

92 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Grovs

Deep linking, attribution, and smart links for React Native.
Part of the Grovs open-source mobile linking platform.

Quick Start · API Reference · Full Docs


The Grovs React Native SDK provides deep linking, universal links, app links, link generation, in-app messaging, revenue tracking, and attribution for your React Native apps.

Features

  • Deep linking & universal links: route users to the right in-app screen, even after install
  • Clipboard deferred deep linking: match an install to a link the user copied before installing
  • Consent support: remember the user’s choice across launches
  • Smart link generation: create trackable links with metadata, custom redirects, and UTM parameters
  • In-app messaging: display messages and announcements from the Grovs dashboard
  • Push notifications: receive push notifications for dashboard-sent messages
  • Revenue tracking: log App Store, Google Play, and custom purchases with automatic attribution
  • Analytics: automatic lifecycle events, custom events, and screen tracking
  • User identity: attach user IDs and attributes for analytics and segmentation
  • Self-hosting support: point the SDK at your own backend
  • Expo support: config plugin for automated native setup

Requirements

  • React Native 0.70+
  • iOS 13.0+
  • Android API 21+ (Android 5.0)

These are the minimums of the native Grovs SDKs. Your React Native version may require a higher one.

The wrapper uses the Grovs native SDKs 3.0.0:

  • iOS: Grovs 3.0 from CocoaPods
  • Android: io.grovs:Grovs:3.0.0 from Maven Central

Installation

# Using npm
npm install react-native-grovs-wrapper

# Using yarn
yarn add react-native-grovs-wrapper

Android dependency

Your app calls Grovs directly from MainApplication and MainActivity, so add the Grovs Android SDK to android/app/build.gradle:

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

It is published on Maven Central, so no extra repository is needed. The Expo config plugin adds this line for you.

iOS dependency

The iOS SDK is added automatically via CocoaPods when you run pod install.

Expo Integration

If you're using Expo with a development build, the config plugin automates all native setup. Add to your app.json:

{
  "plugins": [
    ["react-native-grovs-wrapper", {
      "apiKey": "your-api-key",
      "scheme": "your_app_scheme",
      "useTestEnvironment": false,
      "associatedDomains": ["your_app_host", "your_app_test_host"],
      "baseURL": "https://your-domain.com",
      "clipboardDomains": ["links.your-domain.com"]
    }]
  ]
}
Property Required Description
apiKey Yes Your Grovs API key
scheme Yes Custom URL scheme for deep links
useTestEnvironment No Use test environment (default: false)
associatedDomains No Universal link domains for deep linking
baseURL No Custom base URL for self-hosted backends
clipboardDomains No Extra link hosts accepted for clipboard deferred deep linking. Grovs hosts are always accepted

Then run npx expo prebuild and build with npx expo run:ios / npx expo run:android.

When upgrading an existing Expo integration or changing plugin options, regenerate with npx expo prebuild --clean to refresh the configure calls. Save any manual native edits first.

Note: This requires a development build (expo-dev-client), not Expo Go.

Manual Configuration

Android

1. Initialize the SDK in your MainApplication class:

import com.grovswrapper.GrovsConsent
import io.grovs.Grovs

override fun onCreate() {
    super.onCreate()
    Grovs.configure(
        this, "your-api-key",
        useTestEnvironment = false,
        baseURL = null,                       // or "https://your-domain.com" for self-hosted backends
        autoTrackScreenViews = false,
        clipboardDomains = null,              // or listOf("links.your-domain.com")
        enabled = GrovsConsent.isEnabled(this) // the value JS last passed to setSDK, default true
    )
}

2. Handle incoming links in your MainActivity:

import android.content.Intent
import io.grovs.Grovs

override fun onStart() {
    super.onStart()
    Grovs.onStart(launcherActivity = this)
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    Grovs.onNewIntent(intent, launcherActivity = this)
}

3. Add intent filters to your launcher activity in AndroidManifest.xml:

<!-- Custom URL scheme -->
<intent-filter>
    <data android:scheme="your_app_scheme" android:host="open" />
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
</intent-filter>

<!-- App links (production) -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="your_app_host" />
</intent-filter>

<!-- App links (test) -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="your_app_test_host" />
</intent-filter>

iOS

1. Initialize the SDK in AppDelegate.swift:

import Grovs
import react_native_grovs_wrapper

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Grovs.configure(
        APIKey: "your-api-key",
        useTestEnvironment: false,
        // baseURL: "https://your-domain.com",      // self-hosted backends
        autoTrackScreenViews: false,
        // clipboardDomains: ["links.your-domain.com"],
        enabled: GrovsWrapperSwift.isSDKEnabled(), // the value JS last passed to setSDK, default true
        delegate: self
    )
    Grovs.setDebug(level: .info)
    return true
}

2. Handle incoming links in AppDelegate.swift:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    return Grovs.handleAppDelegate(continue: userActivity, restorationHandler: restorationHandler)
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    return Grovs.handleAppDelegate(open: url, options: options)
}

3. Configure Associated Domains in Xcode:

  1. Select your app target → Signing & Capabilities
  2. Add Associated Domains capability
  3. Add applinks:your_app_host and applinks:your_app_test_host

4. Configure URL scheme:

  1. In Xcode, select your target → Info tab
  2. Under URL Types, click + and add the URL scheme from your Grovs dashboard

Usage

Handle deep links

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

const listener = Grovs.onDeeplinkReceived((response) => {
    console.log('Link:', response.link);
    console.log('Data:', response.data);

    // Route the user based on payload
    if (response.data?.screen === 'product') {
        navigation.navigate('Product', { id: response.data.productId });
    }
});

// When you no longer need the listener
listener.remove();

Set user identity

Grovs.setIdentifier('user-123');
Grovs.setAttributes({
    name: 'John Doe',
    plan: 'premium',
});

Consent

Call setSDK(false) when the user declines analytics and setSDK(true) when they accept. The wrapper remembers the last value, and the native configure call reads it on the next launch, so a user who declined stays opted out from the first frame of the next cold start.

While disabled, the SDK blocks new authentication, event and purchase requests and cancels work in progress. Identifier, attributes, push token and screen aliases you set are kept and sent once the SDK is enabled again. On Android, a link opened while disabled is delivered as soon as you enable the SDK.

Grovs.setSDK(await consentStore.hasAnalyticsConsent());

generateLink, numberOfUnreadMessages and displayMessages reject with code SDK_DISABLED while the SDK is disabled.

Clipboard deferred deep linking

If a user copies a Grovs link before installing, the SDK checks the clipboard once on first launch, only when fingerprint matching found nothing, and delivers the match through onDeeplinkReceived. It can arrive several seconds after launch. On iOS the system shows its paste notice the first time. Only links on Grovs hosts and the clipboardDomains you configure are accepted.

Link Generation

Create smart links with metadata, payload data, and tracking parameters:

try {
    const link = await Grovs.generateLink({
        title: 'Check out this product',
        subtitle: 'Limited time offer',
        imageURL: 'https://example.com/image.jpg',
        data: { productId: '12345', screen: 'product_detail' },
        tags: ['promotion', 'share'],
        customRedirects: {
            android: { link: 'https://example.com/android', open_if_app_installed: true },
            ios: { link: 'https://example.com/ios', open_if_app_installed: true },
            desktop: { link: 'https://example.com/desktop', open_if_app_installed: false },
        },
        showPreviewIos: false,
        showPreviewAndroid: false,
        tracking: { utm_campaign: 'spring_sale', utm_source: 'in_app', utm_medium: 'share_button' },
        copyToClipboardIos: true,      // landing page copies the link so the install can be matched
        copyToClipboardAndroid: true,  // leave undefined to inherit the project default
    });
    console.log('Generated:', link);
} catch (error) {
    console.error('Error:', error);
}

The older positional form generateLink(title, subtitle, imageURL, data, tags, customRedirects, showPreviewIos, showPreviewAndroid, tracking, copyToClipboardIos, copyToClipboardAndroid) still works.

Leave the copy flags undefined to inherit the project default.

Messages

If console messages have automatic display enabled in your dashboard, they will appear in your app without any additional integration.

Push notifications

Pass the FCM token to receive push notifications for dashboard-sent messages:

import messaging from '@react-native-firebase/messaging';

const token = await messaging().getToken();
if (token) {
    Grovs.setPushToken(token);
}

Upload your Firebase or APNs credentials in the Grovs dashboard.

Display messages

// Show the messages list as a modal
await Grovs.displayMessages();

// Get unread count for badges
const count = await Grovs.numberOfUnreadMessages();
console.log(`Unread: ${count}`);

Revenue Tracking

Revenue tracking is currently in beta.

Setup

  1. Enable revenue tracking in the Grovs dashboard under Settings → Revenue Tracking
  2. Configure platform notifications:
    • Android: Set up Google Play Real-Time Developer Notifications
    • iOS: Configure App Store Server Notifications in App Store Connect

Platform store purchases

// iOS: StoreKit 2 transaction id
const success = await Grovs.logInAppPurchase({ transactionId: '123456789' });

// Android: Play Billing purchase original JSON
const success = await Grovs.logInAppPurchase({
  originalJson: purchase.originalJson,
});

The SDK automatically extracts price, currency, and product info. Duplicates are filtered.

Custom purchases

const success = await Grovs.logCustomPurchase(
    'buy',              // type: 'buy' | 'cancel' | 'refund'
    999,                // priceInCents: $9.99
    'USD',              // currency code
    'premium_monthly',  // product identifier
);

Use 'cancel' and 'refund' types for cancellations and refunds. For store purchases, these are detected automatically via platform server notifications.

Analytics

Automatic events

The native SDKs automatically capture lifecycle events: install, reinstall, app_open, reactivation, and time_spent. No setup required.

Custom events

Grovs.track('purchase', { item_id: 'sku-42', price: 19.99 }, ['promo']);

Event names must not be empty or use a reserved system name (view, open, install, reinstall, app_open, time_spent, reactivation, user_referred, custom, screen_view). Properties are dropped if they serialize to more than 8KB, and tags are capped at 20 (255 characters each).

Global tags

Attach tags to every subsequently tracked event:

Grovs.setGlobalTags(['beta']);

// Clear global tags
Grovs.setGlobalTags();

Screen tracking

With React Navigation, one line enables automatic screen view tracking:

import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import Grovs from 'react-native-grovs-wrapper';

function App() {
  const navigationRef = useNavigationContainerRef();
  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => Grovs.startScreenTracking(navigationRef)}>
      {/* navigators */}
    </NavigationContainer>
  );
}

startScreenTracking returns an unsubscribe function, and calling it again replaces the previous subscription: screens are never double-tracked. Consecutive duplicate screen views within 1 second are deduplicated natively.

For Expo Router or custom navigators, track screens manually:

Grovs.trackScreenView('Checkout', { section: 'payment' });

Map screen names to friendly names shown in the Grovs dashboard:

Grovs.setScreenAliases({ Home: 'Home Page' });

Native screen tracking

The native SDKs' own automatic screen tracking only sees the single React Native host Activity/ViewController, so it should be disabled in React Native apps:

  • Expo: the config plugin disables it automatically. If you're upgrading the plugin, re-run npx expo prebuild --clean.
  • Manual / bare React Native: pass autoTrackScreenViews: false in the native configure calls:
// iOS
Grovs.configure(APIKey: "...", useTestEnvironment: false, autoTrackScreenViews: false, enabled: GrovsWrapperSwift.isSDKEnabled(), delegate: self)
// Android
Grovs.configure(this, "API_KEY", useTestEnvironment = false, baseURL = null, autoTrackScreenViews = false, clipboardDomains = null, enabled = GrovsConsent.isEnabled(this))

API Reference

Key Methods

Method Description
onDeeplinkReceived(callback) Register deep link listener (returns { remove })
setSDK(enabled) Enable or disable the SDK. Remembered across launches
setDebug(level) Set logging level ('info', 'error')
setPushToken(token) Set FCM/APNs push token
setIdentifier(identifier) Set user ID for dashboard and reports
setAttributes(attributes) Set user attributes for analytics
generateLink(options) Generate a smart link (GenerateLinkOptions; positional form still supported). Rejects with SDK_DISABLED while disabled
displayMessages() Show messages modal. Rejects with SDK_DISABLED while disabled
numberOfUnreadMessages() Get unread message count. Rejects with SDK_DISABLED while disabled
logInAppPurchase(purchase) Log a store purchase ({ transactionId } on iOS, { originalJson } on Android)
logCustomPurchase(type, priceInCents, currency, productId, startDate?) Log a custom purchase
track(name, properties, tags) Track a custom analytics event
trackScreenView(screenName, properties) Track a screen view
setGlobalTags(tags) Attach tags to every subsequent event (call with no args to clear)
setScreenAliases(aliases) Map screen names to friendly dashboard names
startScreenTracking(navigationRef) Auto-track React Navigation screen changes (returns unsubscribe)

Full API reference: docs.grovs.io/docs/sdk/react-native/api-reference

Example App

This repository has two example apps that use the local wrapper:

See CONTRIBUTING.md for how to run them. A standalone demo project is also available at grovs-io/grovs-react-native-example-app.

Migration Guides

Documentation

Full documentation at docs.grovs.io.

Support

For technical support and inquiries, contact support@grovs.io.

License

This project is licensed under the MIT License: see LICENSE for details.

About

Deep linking and attribution SDK for React Native. Universal Links, App Links, deferred deep linking, and revenue tracking for iOS and Android. Open-source, Expo-compatible alternative to Branch.io and Firebase Dynamic Links.

Topics

Resources

Code of conduct

Contributing

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages