Socket
Book a DemoInstallSign in
Socket

@vtex/activity-flow

Package Overview
Dependencies
Maintainers
54
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vtex/activity-flow

VTEX Activity Flow Mobile RUM for React Native apps focused in Android and iOS.

latest
npmnpm
Version
0.0.6
Version published
Maintainers
54
Created
Source

Activity Flow

VTEX Activity Flow Mobile RUM for React Native apps focused in Android and iOS.

This plugin coupled to an app, aims to:

  • Track user navigation between app pages and send events.

AF Dependencies

The @react-native-async-storage/async-storage library is required because it provides a persistent key-value storage system that is essential for tracking and storing user navigation data locally. This ensures that the Activity Flow plugin can function correctly, even in scenarios where network connectivity is temporarily unavailable. Installing it directly ensures that the native dependencies are properly linked and available in your app.

The app must use React Navigation or Expo Router, as Activity Flow relies on the useFocusEffect hook from React Navigation to detect page changes. For guidance on testing with Jest and avoiding issues, refer to the React Navigation documentation.

Installation

npm install @vtex/activity-flow @react-native-async-storage/async-storage

# or
yarn add @vtex/activity-flow @react-native-async-storage/async-storage

And running pod install (for iOS) ensures the native dependencies are properly synced.

 cd ios && pod install && cd ../

Usage

Initializing the Activity Flow Plugin

To use the Activity Flow plugin, you need to initialize it in your React Native application. This is typically done in the main entry file of your app (e.g., App.js or index.js).

import { initActivityFlow } from '@vtex/activity-flow';

function App(): React.JSX.Element {
  // Initialize the Activity Flow plugin with your VTEX account name
  initActivityFlow({
    accountName: 'your-account-name', // Replace with your VTEX account name
  });

  return (
    // Your app components
  );
}

Screen view event

To use the usePageViewObserver hook, you need to integrate it with the NavigationContainer and ensure it is set up correctly to track navigation state changes.

import { usePageViewObserver } from '@vtex/activity-flow';

The NavigationContainer is the root component for React Navigation. You need to create a ref and pass it to the NavigationContainer so that the usePageViewObserver hook can access the navigation state.

Also, the observer needs the account name to identify the source of the event.

  • Step 1:Create a ref for the NavigationContainer
const navigationRef = useRef(null);
  • Step 2: Use the reference created before.
return (
    <NavigationContainer ref={navigationRef}>
        // Stack configurations
    </NavigationContainer>
  );
  • Step 3: Use the hook to track page views. Pass the account name and navigation reference.
usePageViewObserver({
  navigationRef: navigationRef,
});

The Activity Flow SDK now automatically captures query parameters from deep links and includes them in page view events. Also, you need to configure the deep links in your app, depending on the platform. For Android check the Android Setup section. For iOS check the iOS Setup section.

Android Setup

To enable deep link handling on Android, you need to add intent filters to your AndroidManifest.xml file. Each route that can be accessed via deep link requires its own intent filter.

Location: android/app/src/main/AndroidManifest.xml

Add intent filters inside the <activity> tag for your main activity:

<!-- Example for HTTPS deep links -->
<intent-filter>
  <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_DOMAIN}" android:pathPrefix="{YOUR_APP_ROUTE}" />
</intent-filter>

<!-- Example for custom URL scheme -->
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="{YOUR_CUSTOM_SCHEME}" />
</intent-filter>

Important: For each route in your app that should be accessible via deep link, add a new intent filter. The main difference between intent filters is the android:pathPrefix attribute, which specifies the route path.

Example with multiple routes:

<!-- Route for product pages -->
<intent-filter>
  <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="mystore.com" android:pathPrefix="/product" />
</intent-filter>

<!-- Route for checkout -->
<intent-filter>
  <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="mystore.com" android:pathPrefix="/checkout" />
</intent-filter>

iOS Setup

iOS deep link configuration requires changes in two files:

Step 1: Configure Info.plist

Add the following configuration to your ios/{YourApp}/Info.plist file to register your custom URL scheme:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>{YOUR_BUNDLE_URL_SCHEME}</string>
    </array>
    <key>CFBundleURLName</key>
    <string>{YOUR_BUNDLE_URL_NAME}</string>
  </dict>
</array>

Example:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
    <key>CFBundleURLName</key>
    <string>com.mycompany.myapp</string>
  </dict>
</array>

Step 2: Configure AppDelegate

Modify your ios/{YourApp}/AppDelegate.swift (or AppDelegate.mm if using Objective-C) to handle incoming deep links.

Important: The following code handles deep links for both cold starts (when the app is not running) and warm starts (when the app is already running).

Add the following code to your AppDelegate:

import UIKit
import React

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {

    // ... your existing setup code ...

    // Capture initial URL if app was launched with a deep link (cold start)
    if let initialURL = launchOptions?[UIApplication.LaunchOptionsKey.url] as? URL {
      NotificationCenter.default.post(
        name: NSNotification.Name("RCTOpenURLNotification"),
        object: nil,
        userInfo: ["url": initialURL]
      )
    }

    return true
  }

  /**
   * Handles incoming URLs from Custom URL Schemes (e.g., myapp://path)
   * This is called when the app is opened via a custom URL scheme.
   */
  func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey : Any] = [:]
  ) -> Bool {
    // Post notification for Activity Flow's CaptureDeepLinkModule
    NotificationCenter.default.post(
      name: NSNotification.Name("RCTOpenURLNotification"),
      object: nil,
      userInfo: ["url": url]
    )

    // Pass the URL to React Native's standard linking manager
    return RCTLinkingManager.application(app, open: url, options: options)
  }

  /**
   * Handles incoming URLs from Universal Links (e.g., https://mystore.com/product)
   * Universal Links allow your app to be opened from standard HTTPS URLs.
   */
  func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
  ) -> Bool {
    // Check if the activity is a web browsing activity (Universal Link)
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
      // Post notification for Activity Flow's CaptureDeepLinkModule
      if let url = userActivity.webpageURL {
        NotificationCenter.default.post(
          name: NSNotification.Name("RCTOpenURLNotification"),
          object: nil,
          userInfo: ["url": url]
        )
      }

      // Pass the user activity to React Native's standard linking manager
      return RCTLinkingManager.application(
        application,
        continue: userActivity,
        restorationHandler: restorationHandler
      )
    }
    return false
  }
}

Note: Make sure to import RCTLinkingManager at the top of your AppDelegate file if it's not already imported.

  • Android: https://mystore.com/product?id=12345queryParams = { id: '12345' }

  • iOS: myapp://checkout?orderId=abc123&step=paymentqueryParams = { orderId: 'abc123', step: 'payment' }

The query parameters are automatically captured and included in the page view events, providing better tracking and analytics for deep link attribution.

Ads events

To track ad events Activity Flow, get the handlers from the useAdsTracker hook. The hook accepts the ad parameters as an Object. All the ad parameters, like adId, need to be passed on the hook like the example.

const { handlePress, viewRef } = useAdsTracker(
  {
    adId: '123',
    // ... other ads parameters
  },
  true // enables the hook, optional, default value is true
);

To capture impression events, simply call useAdsTracker with the enabled parameter set to true (which is the default). This is helpful for components that may or may not serve as ads, enabling or disabling all the ad events capture. To capture click events, the handlePress needs to be called besides the press property of the component. To capture view events, use the viewRef on the' ref' property of the desired ad viewable component. Next, an example use of the useAdsTracker hook.

return (
  <View ref={viewRef}>
    <TouchableOpacity
      style={styles.button}
      onPress={() => {
        navigation.navigate('/checkout');
        handlePress();
      }}
    >
      <Text style={styles.buttonText}>Buy</Text>
    </TouchableOpacity>
  </View>
);

AFAdsTracker component

Instead of manually using the hook with a ref and handlers, you can simply wrap your ad component with the AFAdsTracker component to capture the events automatically. Using this component, the previous example would look like this:

return (
  <AFAdsTracker params={{ adId: '123', foo: 'bar' }}>
    <TouchableOpacity
      style={styles.button}
      onPress={() => {
        navigation.navigate('/checkout');
      }}
    >
      <Text style={styles.buttonText}>Buy</Text>
    </TouchableOpacity>
  </AFAdsTracker>
);

The AFAdsTracker captures click events using onTouchEndCapture, which fires during the event's capture phase. To align your ad's own click logic with this, you should use the standard onTouchEnd prop, which fires during the subsequent bubble phase.

Complete Example

You can see the example with the implementation in App.jsx(tsx) here.

Problems and Solutions

1. Missing @react-native-async-storage/async-storage dependency

or [@RNC/AsyncStorage]: NativeModule: AsyncStorage is null.

Problem: The app fails to build or run because the @react-native-async-storage/async-storage dependency is not properly installed or linked.

Solution: Ensure that your project's package.json explicitly includes @react-native-async-storage/async-storage as a dependency, even if it is indirectly required by other dependencies. Run the following commands to install and link the dependency:

yarn add @react-native-async-storage/async-storage
cd ios && pod install && cd ../

Problem: The Activity Flow plugin is not sending events as expected.

Solution:

  • Verify that the accountName provided to the initActivityFlow function is correct.
  • Check your network connectivity to ensure events can be sent.
  • Confirm that the plugin is properly installed and initialized in your app.

2. iOS Build Issues After Installation

Problem: The iOS build fails after installing the dependencies.

Solution: Run the following commands to ensure all native dependencies are properly linked and synced:

cd ios && pod install && cd ../

If the issue persists, clean the build folder and rebuild the project:

cd ios && xcodebuild clean && cd ../

3. Android Build Issues After Installation

Problem: The Android build fails due to missing dependencies or incorrect configurations.

Solution:

  • Ensure that the @react-native-async-storage/async-storage dependency is installed.
  • Sync your Gradle files by running:
cd android && ./gradlew clean && cd ../
  • Rebuild the project to resolve any dependency issues.

If you encounter any other issues, please refer to the official documentation or open an issue in the plugin's repository.

License

MIT

Keywords

react-native

FAQs

Package last updated on 10 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts