Giter Club home page Giter Club logo

analytics-react-native's Introduction

@segment/analytics-react-native

๐ŸŽ‰ Flagship ๐ŸŽ‰

This library is one of Segmentโ€™s most popular Flagship libraries. It is actively maintained by Segment, benefitting from new feature releases and ongoing support.

The hassle-free way to add Segment analytics to your React-Native app.

โš ๏ธ This readme covers analytics-react-native 2.0.0 and greater. The code and readme for analytics-react-native versions below 2.0.0 can be found on the analytics-react-native-v1 branch. On May 15, 2023, Segment will end support for Analytics React Native Classic, which includes versions 1.5.1 and older. Upgrade to Analytics React Native 2.0.

Table of Contents

Installation

Install @segment/analytics-react-native, @segment/sovran-react-native and react-native-get-random-values:

yarn add @segment/analytics-react-native @segment/sovran-react-native react-native-get-random-values @react-native-async-storage/async-storage 
# or
npm install --save @segment/analytics-react-native @segment/sovran-react-native react-native-get-random-values @react-native-async-storage/async-storage 

Note: @react-native-async-storage/async-storage is an optional dependency. If you wish to use your own persistence layer you can use the storePersistor option when initializing the client. Make sure you always have a persistor (either by having AsyncStorage package installed or by explicitly passing a value), else you might get unexpected side-effects like multiple 'Application Installed' events. Read more Client Options

For iOS, install native modules with:

npx pod-install

โš ๏ธ For Android, you will have to add some extra permissions to your AndroidManifest.xml.

Expo

๐Ÿš€ @segment/analytics-react-native 2.0 is compatible with Expo's Custom Dev Client and EAS builds without any additional configuration. Destination Plugins that require native modules may require custom Expo Config Plugins.

โš ๏ธ @segment/analytics-react-native 2.0 is not compatible with Expo Go.

Permissions

Android In your app's `AndroidManifest.xml` add the below line between the `` tags.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Migrating

See the Migration Guide for a detailed walkthrough of the changes you will need to make when upgrading to analytics-react-native 2.0

Usage

Setting up the client

The package exposes a method called createClient which we can use to create the Segment Analytics client. This central client manages all our tracking events.

import { createClient } from '@segment/analytics-react-native';

const segmentClient = createClient({
  writeKey: 'SEGMENT_API_KEY'
});

You must pass at least the writeKey. Additional configuration options are listed below:

Client Options

Name Default Description
writeKey (REQUIRED) '' Your Segment API key.
collectDeviceId false Set to true to automatically collect the device Id.from the DRM API on Android devices.
debug true* When set to false, it will not generate any logs.
logger undefined Custom logger instance to expose internal Segment client logging.
flushAt 20 How many events to accumulate before sending events to the backend.
flushInterval 30 In seconds, how often to send events to the backend.
flushPolicies undefined Add more granular control for when to flush, see Adding or removing policies. Mutually exclusive with flushAt/flushInterval
maxBatchSize 1000 How many events to send to the API at once
trackAppLifecycleEvents false Enable automatic tracking for app lifecycle events: application installed, opened, updated, backgrounded)
trackDeepLinks false Enable automatic tracking for when the user opens the app via a deep link (Note: Requires additional setup on iOS, see instructions)
defaultSettings undefined Settings that will be used if the request to get the settings from Segment fails. Type: SegmentAPISettings
autoAddSegmentDestination true Set to false to skip adding the SegmentDestination plugin
storePersistor undefined A custom persistor for the store that analytics-react-native leverages. Must match Persistor interface exported from sovran-react-native.
proxy undefined proxy is a batch url to post to instead of 'https://api.segment.io/v1/b'.
errorHandler undefined Create custom actions when errors happen, see Handling errors
cdnProxy undefined Sets an alternative CDN host for settings retrieval

* The default value of debug will be false in production.

iOS Deep Link Tracking Setup

Note: This is only required for iOS if you are using the trackDeepLinks option. Android does not require any additional setup

To track deep links in iOS you must add the following to your AppDelegate.m file:

  #import <segment_analytics_react_native-Swift.h>
  
  ...
  
- (BOOL)application:(UIApplication *)application
            openURL: (NSURL *)url
            options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  
  [AnalyticsReactNative trackDeepLink:url withOptions:options];  
  return YES;
}

If you are using Expo, you need to create an AppDelegateSubscriber, make sure to include segment_analytics_react_native in your podspec file. The rest of the code looks like this:

    import segment_analytics_react_native

    ...

    open func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
       AnalyticsReactNative.trackDeepLink(url: url as NSURL, options: options)
       return false
   }

Native AnonymousId

If you need to generate an anonymousId either natively or before the Analytics React Native package is initialized, you can send the anonymousId value from native code. The value has to be generated and stored by the caller. For reference, you can find a working example in the app and reference the code below:

iOS

...
#import <segment_analytics_react_native-Swift.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  ...
  // generate your anonymousId value
  // dispatch it across the bridge

  [AnalyticsReactNative setAnonymousId: @"My-New-Native-Id"];
  return yes
}

Android

// MainApplication.java
...
import com.segmentanalyticsreactnative.AnalyticsReactNativePackage;

...
private AnalyticsReactNativePackage analytics = new AnalyticsReactNativePackage();

...
   @Override
    protected List<ReactPackage> getPackages() {
      @SuppressWarnings("UnnecessaryLocalVariable")
      List<ReactPackage> packages = new PackageList(this).getPackages();
      // AnalyticsReactNative will be autolinked by default, but to send the anonymousId before RN startup you need to manually link it to store a reference to the package
      packages.add(analytics);
      return packages;
    }
...
  @Override
  public void onCreate() {
    super.onCreate();
    ...

  // generate your anonymousId value
  // dispatch it across the bridge

  analytics.setAnonymousId("My-New-Native-Id");
  }

Usage with hooks

In order to use the useAnalytics hook within the application, we will additionally need to wrap the application in an AnalyticsProvider. This uses the Context API and will allow access to the analytics client anywhere in the application

import {
  createClient,
  AnalyticsProvider,
} from '@segment/analytics-react-native';

const segmentClient = createClient({
  writeKey: 'SEGMENT_API_KEY'
});

const App = () => (
  <AnalyticsProvider client={segmentClient}>
    <Content />
  </AnalyticsProvider>
);

useAnalytics()

The client methods will be exposed via the useAnalytics() hook:

import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { useAnalytics } from '@segment/analytics-react-native';

const Button = () => {
  const { track } = useAnalytics();
  return (
    <TouchableOpacity
      style={styles.button}
      onPress={() => {
        track('Awesome event');
      }}
    >
      <Text style={styles.text}>Press me!</Text>
    </TouchableOpacity>
  );
};

Usage without hooks

The tracking events can also be used without hooks by calling the methods directly on the client:

import {
  createClient,
  AnalyticsProvider,
} from '@segment/analytics-react-native';

// create the client once when the app loads
const segmentClient = createClient({
  writeKey: 'SEGMENT_API_KEY'
});

// track an event using the client instance
segmentClient.track('Awesome event');

Client methods

Track

The track method is how you record any actions your users perform, along with any properties that describe the action.

Method signature:

track: (event: string, properties?: JsonMap) => void;

Example usage:

const { track } = useAnalytics();

track('View Product', {
  productId: 123,
  productName: 'Striped trousers',
});

Screen

The screen call lets you record whenever a user sees a screen in your mobile app, along with any properties about the screen.

Method signature:

screen: (name: string, properties?: JsonMap) => void;

Example usage:

const { screen } = useAnalytics();

screen('ScreenName', {
  productSlug: 'example-product-123',
});

For setting up automatic screen tracking, see the instructions below.

Identify

The identify call lets you tie a user to their actions and record traits about them. This includes a unique user ID and any optional traits you know about them like their email, name, etc. The traits option can include any information you might want to tie to the user, but when using any of the reserved user traits, you should make sure to only use them for their intended meaning.

Method signature:

identify: (userId: string, userTraits?: JsonMap) => void;

Example usage:

const { identify } = useAnalytics();

identify('user-123', {
  username: 'MisterWhiskers',
  email: '[email protected]',
  plan: 'premium',
});

Group

The group API call is how you associate an individual user with a groupโ€”be it a company, organization, account, project, team or whatever other crazy name you came up with for the same concept! This includes a unique group ID and any optional group traits you know about them like the company name industry, number of employees, etc. The traits option can include any information you might want to tie to the group, but when using any of the reserved group traits, you should make sure to only use them for their intended meaning.

Method signature:

group: (groupId: string, groupTraits?: JsonMap) => void;

Example usage:

const { group } = useAnalytics();

group('some-company', {
  name: 'Segment',
});

Alias

The alias method is used to merge two user identities, effectively connecting two sets of user data as one. This is an advanced method, but it is required to manage user identities successfully in some of our destinations.

Method signature:

alias: (newUserId: string) => void;

Example usage:

const { alias } = useAnalytics();

alias('user-123');

Reset

The reset method clears the internal state of the library for the current user and group. This is useful for apps where users can log in and out with different identities over time.

Note: Each time you call reset, a new AnonymousId is generated automatically.

And when false is passed as an argument in reset method, it will skip resetting the anonymousId (but reset the rest of the user date).

Method signature:

reset: (resetAnonymousId = true) => void;

Example usage:

const { reset } = useAnalytics();

reset();

reset(resetAnonymousId = false);

Flush

By default, the analytics will be sent to the API after 30 seconds or when 20 items have accumulated, whatever happens sooner, and whenever the app resumes if the user has closed the app with some events unsent. These values can be modified by the flushAt and flushInterval config options. You can also trigger a flush event manually.

Method signature:

flush: () => Promise<void>;

Example usage:

const { flush } = useAnalytics();

flush();

(Advanced) Cleanup

You probably don't need this!

In case you need to reinitialize the client, that is, you've called createClient more than once for the same client in your application lifecycle, use this method on the old client to clear any subscriptions and timers first.

let client = createClient({
  writeKey: 'KEY'
});

client.cleanup();

client = createClient({
  writeKey: 'KEY'
});

If you don't do this, the old client instance would still exist and retain the timers, making all your events fire twice.

Ideally, you shouldn't need this though, and the Segment client should be initialized only once in the application lifecycle.

Automatic screen tracking

Sending a screen() event with each navigation action will get tiresome quick, so you'll probably want to track navigation globally. The implementation will be different depending on which library you use for navigation. The two main navigation libraries for React Native are React Navigation and React Native Navigation.

React Navigation

Our example app is set up with screen tracking using React Navigation, so you can use it as a guide.

Essentially what we'll do is find the root level navigation container and call screen() whenever user has navigated to a new screen.

Find the file where you've used the NavigationContainer - the main top level container for React Navigation. In this component, create 2 new refs to store the navigation object and the current route name:

const navigationRef = useRef(null);
const routeNameRef = useRef(null);

Next, pass the ref to NavigationContainer and a function in the onReady prop to store the initial route name. Finally, pass a function in the onStateChange prop of your NavigationContainer that checks for the active route name and calls client.screen() if the route has changes. You can pass in any additional screen parameters as the second argument for screen call as needed.

<NavigationContainer
  ref={navigationRef}
  onReady={() => {
    routeNameRef.current = navigationRef.current.getCurrentRoute().name;
  }}
  onStateChange={() => {
    const previousRouteName = routeNameRef.current;
    const currentRouteName = navigationRef.current?.getCurrentRoute().name;

    if (previousRouteName !== currentRouteName) {
      segmentClient.screen(currentRouteName);
      routeNameRef.current = currentRouteName;
    }
  }}
>

React Native Navigation

In order to setup automatic screen tracking while using React Native Navigation, you will have to use an event listener. That can be done at the point where you are setting up the root of your application (ie. Navigation.setRoot). There your will need access to your SegmentClient.

// Register the event listener for *registerComponentDidAppearListener*
Navigation.events().registerComponentDidAppearListener(({ componentName }) => {
  segmentClient.screen(componentName);
});

Consent Management

Consent Management is the management of a userโ€™s consent preferences related to privacy. You might be familiar with the Privacy Pop-ups that have become mandated recently that ask the user if he or she consents to the use of certain category of cookies:

Sample CMP UI

The Privacy pop-up asks the user if he or she will consent to the use of cookies and allows the user to customize their consent by turning on/off different categories of cookies.

After the user selects โ€œAllow Allโ€ or โ€œSave Preferencesโ€ a callback is fired and the owner of the website is notified as to the consent preferences of a given user. The website owner must then store that consent preference and abide by it. Any rejected cookies must not be set or read to avoid large fines that can be handed down by government authorities.

Additionally, besides the initial pop-up the website owner must give users a way to later change any preferences they originally selected. This is usually accomplished by providing a link to display the customization screen.

Segment managed CMP

Segment provides a framework for users to integrate any CMP they choose and use the Segment web app to map consent categories to device mode destinations. This information is sent down the analytics-kotlin SDK and stored for later lookup.

Every event that flows through the library will be stamped with the current status according to whatever configured CMP is used. Event stamping is handled by the ConsentManagementPlugin.

Using consent status stamped on the events and the mappings sent down from the Segment web app each event is evaluated and action is taken. Currently the supported actions are:

  • Blocking - This action is implemented by the ConsentBlockingPlugin

Event Stamping

Event stamping is the process of adding the consent status information to an existing event. The information is added to the context object of every event. Below is a before and after example:

Before

{
    "anonymousId": "23adfd82-aa0f-45a7-a756-24f2a7a4c895",
    "type": "track",
    "event": "MyEvent",
    "userId": "u123",
    "timestamp": "2023-01-01T00:00:00.000Z",
    "context": {
        "traits": {
            "email": "[email protected]",
            "phone": "555-555-5555"
        },
        "device": {
            "advertisingId": "7A3CBBA0-BDF5-11E4-8DFC-AA02A5B093DB"
        }
    }
}

After

{
    "anonymousId": "23adfd82-aa0f-45a7-a756-24f2a7a4c895",
    "type": "track",
    "event": "MyEvent",
    "userId": "u123",
    "timestamp": "2023-01-01T00:00:00.000Z",
    "context": {
        "traits": {
            "email": "[email protected]",
            "phone": "555-555-5555"
        },
        "device": {
            "advertisingId": "7A3CBBA0-BDF5-11E4-8DFC-AA02A5B093DB"
        },
        "consent": {
            "categoryPreferences": {
                "Advertising": true,
                "Analytics": false,
                "Functional": true,
                "DataSharing": false
            }
        }
    }
}

Segment Consent Preference Updated Event

When notified by the CMP SDK that consent has changed, a track event with name โ€œSegment Consent Preference Updatedโ€ will be emitted. Below is example of what that event will look like:

{
    "anonymousId": "23adfd82-aa0f-45a7-a756-24f2a7a4c895",
    "type": "track",
    "event": "Segment Consent Preference Updated",
    "userId": "u123",
    "timestamp": "2023-01-01T00:00:00.000Z",
    "context": {
        "device": {
            "advertisingId": "7A3CBEA0-BDF5-11E4-8DFC-AA07A5B093DB"
        },
        "consent": {
            "categoryPreferences": {
                "Advertising": true,
                "Analytics": false,
                "Functional": true,
                "DataSharing": false
            }
        }
    }
}

Event Flow

Shows how an event is stamped and later checked for consent

  1. An event is dropped onto the timeline by some tracking call.
  2. The ConsentManagementPlugin consumes the event, stamps it, and returns it.
  3. The event is now stamped with consent information from this point forward.
  4. The event is copied. The copy is consumed by a Destination Plugin and continues down its internal timeline. The original event is returned and continues down the main timeline. a. The stamped event is now on the timeline of the destination plugin. b. The event reaches the ConsentBlockingPlugin which makes a decision as to whether or not to let the event continue down the timeline. c. If the event has met the consent requirements it continues down the timeline.
  5. The event continues down the timeline.

Getting Started

  1. Since the Consent Management Plugin is built into the core Analytics-React-Native SDK, you can simply import it and begin using it without adding any additional Segment dependencies.
import { createClient, ConsentPlugin} from '@segment/analytics-react-native';
  1. From here, you will have to build an Consent Provider integration with your CMP. You can reference our example OneTrust integration here. It is not possible for Segment to support this as an active plugin as OneTrust requires you to use very specific versions of their SDK. However, the functionality is usually unchanged across versions so the example linked above should be almost copy/paste. If you build your own, it needs to imlpement the CategoryConsentProvider interface:
interface CategoryConsentStatusProvider {
  setApplicableCategories(categories: string[]): void;
  getConsentStatus(): Promise<Record<string, boolean>>;
  onConsentChange(cb: (updConsent: Record<string, boolean>) => void): void;
  shutdown?(): void;
}
  1. Add the Consent Provider to the ConsentPlugin() and add ConsentPlugin() to the client. A full example of this setup, including initializing the OneTrust SDK can be found here.
const segment = createClient({
  writeKey: 'SEGMENT_KEY',
  ...<other config options>
});


const myCustomProvider = new MyCustomProvider(MyCMP)
const consentPlugin = new ConsentPlugin(myCustomProvider);

segment.add({ plugin: oneTrustPlugin });
  1. Once the Segment Client and third-party CMP have been initialized, start processing queued events
consentPlugin.start()

Plugins + Timeline architecture

You have complete control over how the events are processed before being uploaded to the Segment API.

In order to customise what happens after an event is created, you can create and place various Plugins along the processing pipeline that an event goes through. This pipeline is referred to as a Timeline.

Plugin Types

Plugin Type Description
before Executed before event processing begins.
enrichment Executed as the first level of event processing.
destination Executed as events begin to pass off to destinations.
after Executed after all event processing is completed. This can be used to perform cleanup operations, etc.
utility Executed only when called manually, such as Logging.

Plugins can have their own native code (such as the iOS-only IdfaPlugin) or wrap an underlying library (such as FirebasePlugin which uses react-native-firebase under the hood)

Destination Plugins

Segment is included as a DestinationPlugin out of the box. You can add as many other DestinationPlugins as you like, and upload events and data to them in addition to Segment.

Or if you prefer, you can pass autoAddSegmentDestination = false in the options when setting up your client. This prevents the SegmentDestination plugin from being added automatically for you.

Adding Plugins

You can add a plugin at any time through the segmentClient.add() method.

import { createClient } from '@segment/analytics-react-native';

import { AmplitudeSessionPlugin } from '@segment/analytics-react-native-plugin-amplitude-session';
import { FirebasePlugin } from '@segment/analytics-react-native-plugin-firebase';
import { IdfaPlugin } from '@segment/analytics-react-native-plugin-idfa';

const segmentClient = createClient({
  writeKey: 'SEGMENT_KEY'
});

segmentClient.add({ plugin: new AmplitudeSessionPlugin() });
segmentClient.add({ plugin: new FirebasePlugin() });
segmentClient.add({ plugin: new IdfaPlugin() });

Writing your own Plugins

Plugins are implemented as ES6 Classes. To get started, familiarise yourself with the available classes in /packages/core/src/plugin.ts.

The available plugin classes are:-

  • Plugin
  • EventPlugin
  • DestinationPlugin
  • UtilityPlugin
  • PlatformPlugin

Any plugins must be an extension of one of these classes.

You can them customise the functionality by overriding different methods on the base class. For example, here is a simple Logger plugin:

// logger.js

import {
  Plugin,
  PluginType,
  SegmentEvent,
} from '@segment/analytics-react-native';

export class Logger extends Plugin {

  // Note that `type` is set as a class property
  // If you do not set a type your plugin will be a `utility` plugin (see Plugin Types above)
  type = PluginType.before;

  execute(event: SegmentEvent) {
    console.log(event);
    return event;
  }
}
// app.js

import { Logger } from './logger';

segmentClient.add({ plugin: new Logger() });

As it overrides the execute() method, this Logger will call console.log for every event going through the Timeline.

Supported Plugins

Refer to the following table for Plugins you can use to meet your tracking needs:

Plugin Package
Adjust @segment/analytics-react-native-plugin-adjust
Amplitude Sessions @segment/analytics-react-native-plugin-amplitude-session
AppsFlyer @segment/analytics-react-native-plugin-appsflyer
Braze @segment/analytics-react-native-plugin-braze
Braze Middleware (Cloud Mode) @segment/analytics-react-native-plugin-braze-middleware
CleverTap @segment/analytics-react-native-plugin-clevertap
Facebook App Events @segment/analytics-react-native-plugin-facebook-app-events
Firebase @segment/analytics-react-native-plugin-firebase
FullStory @fullstory/segment-react-native-plugin-fullstory
IDFA @segment/analytics-react-native-plugin-idfa
Mixpanel @segment/analytics-react-native-plugin-mixpanel
Sprig @sprig-technologies/analytics-react-native-plugin-sprig
Taplytics @taplytics/segment-react-native-plugin-taplytics
Android Advertising ID @segment/analytics-react-native-plugin-advertising-id
Survicate @survicate/analytics-react-native-survicate

Controlling Upload With Flush Policies

To more granurily control when events are uploaded you can use FlushPolicies. This will override any setting on flushAt and flushInterval, but you can use CountFlushPolicy and TimerFlushPolicy to have the same behaviour respectively.

A Flush Policy defines the strategy for deciding when to flush, this can be on an interval, on a certain time of day, after receiving a certain number of events or even after receiving a particular event. This gives you even more flexibility on when to send event to Segment.

To make use of flush policies you can set them in the configuration of the client:

const client = createClient({
  // ...
  flushPolicies: [
    new CountFlushPolicy(5),
    new TimerFlushPolicy(500),
    new StartupFlushPolicy(),
  ],
});

You can set several policies at a time. Whenever any of them decides it is time for a flush it will trigger an upload of the events. The rest get reset so that their logic restarts after every flush.

That means only the first policy to reach shouldFlush gets to trigger a flush at a time. In the example above either the event count gets to 5 or the timer reaches 500ms, whatever comes first will trigger a flush.

We have several standard FlushPolicies:

  • CountFlushPolicy triggers whenever a certain number of events is reached
  • TimerFlushPolicy triggers on an interval of milliseconds
  • StartupFlushPolicy triggers on client startup only
  • BackgroundFlushPolicy triggers when the app goes into the background/inactive.

Adding or removing policies

One of the main advatanges of FlushPolicies is that you can add and remove policies on the fly. This is very powerful when you want to reduce or increase the amount of flushes.

For example you might want to disable flushes if you detect the user has no network:

import NetInfo from "@react-native-community/netinfo";

const policiesIfNetworkIsUp = [
  new CountFlushPolicy(5),
  new TimerFlushPolicy(500),
];

// Create our client with our policies by default
const client = createClient({
  // ...
  flushPolicies: policies,
});

// If we detect the user disconnects from the network remove all flush policies, 
// that way we won't keep attempting to send events to segment but we will still 
// store them for future upload.
// If the network comes back up we add the policies back
const unsubscribe = NetInfo.addEventListener((state) => {
  if (state.isConnected) {
    client.addFlushPolicy(...policiesIfNetworkIsUp);
  } else {
    client.removeFlushPolicy(...policiesIfNetworkIsUp)
  }
});

Creating your own flush policies

You can create a custom FlushPolicy special for your application needs by implementing the FlushPolicy interface. You can also extend the FlushPolicyBase class that already creates and handles the shouldFlush value reset.

A FlushPolicy only needs to implement 2 methods:

  • start(): Executed when the flush policy is enabled and added to the client. This is a good place to start background operations, make async calls, configure things before execution
  • onEvent(event: SegmentEvent): Gets called on every event tracked by your client
  • reset(): Called after a flush is triggered (either by your policy, by another policy or manually)

They also have a shouldFlush observable boolean value. When this is set to true the client will atempt to upload events. Each policy should reset this value to false according to its own logic, although it is pretty common to do it inside the reset method.

export class FlushOnScreenEventsPolicy extends FlushPolicyBase {

  onEvent(event: SegmentEvent): void {
    // Only flush when a screen even happens
    if (event.type === EventType.ScreenEvent) {
      this.shouldFlush.value = true;
    }
  }

  reset(): void {
    // Superclass will reset the shouldFlush value so that the next screen event triggers a flush again
    // But you can also reset the value whenever, say another event comes in or after a timeout
    super.reset();
  }
}

Handling errors

You can handle analytics client errors through the errorHandler option.

The error handler configuration receives a function which will get called whenever an error happens on the analytics client. It will receive an argument of SegmentError type.

You can use this error handling to trigger different behaviours in the client when a problem occurs. For example if the client gets rate limited you could use the error handler to swap flush policies to be less aggressive:

const flushPolicies = [new CountFlushPolicy(5), new TimerFlushPolicy(500)];

const errorHandler = (error: SegmentError) => {
  if (error.type === ErrorType.NetworkServerLimited) {
    // Remove all flush policies
    segmentClient.removeFlushPolicy(...segmentClient.getFlushPolicies());
    // Add less persistent flush policies
    segmentClient.addFlushPolicy(
      new CountFlushPolicy(100),
      new TimerFlushPolicy(5000)
    );
  }
};

const segmentClient = createClient({
  writeKey: 'WRITE_KEY',
  trackAppLifecycleEvents: true,
  collectDeviceId: true,
  debug: true,
  trackDeepLinks: true,
  flushPolicies: flushPolicies,
  errorHandler: errorHandler,
});

The reported errors can be of any of the ErrorType enum values.

Reporting errors from plugins

Plugins can also report errors to the handler by using the .reportInternalError function of the analytics client, we recommend using the ErrorType.PluginError for consistency, and attaching the innerError with the actual exception that was hit:

  try {
    distinctId = await mixpanel.getDistinctId();
  } catch (e) {
    analytics.reportInternalError(
      new SegmentError(ErrorType.PluginError, 'Error: Mixpanel error calling getDistinctId', e)
    );
    analytics.logger.warn(e);
  }

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

Code of Conduct

Before contributing, please also see our code of conduct.

License

MIT

analytics-react-native's People

Contributors

509dave16 avatar ajenkins avatar alanjcharles avatar ashton-huxtable avatar bsneed avatar carloskelly13 avatar dependabot[bot] avatar f2prateek avatar fathyb avatar gbyesiltas avatar github-actions[bot] avatar hvardhan-unth avatar jedashford avatar kelset avatar kerumen avatar konoufo avatar leopic avatar lorex7 avatar luisfmsouza avatar maherzaidoune avatar manueljtejada avatar niallzato avatar oscb avatar prayansh avatar semantic-release-bot avatar shubhamdeol avatar teekirol avatar tlalfano avatar wkunert avatar zikaari avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

analytics-react-native's Issues

Ios build is failing

I add the libraries in the coca pod And i have this error:

fatal error: โ€˜Segment-Mixpanel/SEGMixpanelIntegrationFactory.hโ€™ file not found
#import <Segment-Mixpanel/SEGMixpanelIntegrationFactory.h>

Project with path ':@segment_analytics-react-native' could not be found in project

FAILURE: Build failed with an exception.

  • Where:
    Build file '.../node_modules/@segment/analytics-react-native-google-analytics/android/build.gradle' line: 43

  • What went wrong:
    A problem occurred evaluating project ':@segment/analytics-react-native-google-analytics'.

Project with path ':@segment_analytics-react-native' could not be found in project ':@segment/analytics-react-native-google-analytics'.

Android kotlin runtime error

I am having trouble using this library on Android.
I am currently running React-Native 0.57.8, and therefor using version 0.0.1-beta.3 of this library.
This is the last version before RN 0.59 support was added.

I am getting the following error:

java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
at com.segment.analytics.reactnative.core.RNAnalyticsPackage.createViewManagers(RNAnalyticsPackage.kt)
at com.facebook.react.ReactInstanceManager.getOrCreateViewManagers(ReactInstanceManager.java:756)
at com.facebook.react.CoreModulesPackage.createUIManager(CoreModulesPackage.java:170)
at com.facebook.react.CoreModulesPackage.access$200(CoreModulesPackage.java:53)
at com.facebook.react.CoreModulesPackage$7.get(CoreModulesPackage.java:128)
at com.facebook.react.CoreModulesPackage$7.get(CoreModulesPackage.java:125)
at com.facebook.react.NativeModuleRegistryBuilder.processPackage(NativeModuleRegistryBuilder.java:61)
at com.facebook.react.ReactInstanceManager.processPackage(ReactInstanceManager.java:1173)
at com.facebook.react.ReactInstanceManager.processPackages(ReactInstanceManager.java:1143)
at com.facebook.react.ReactInstanceManager.createReactContext(ReactInstanceManager.java:1085)
at com.facebook.react.ReactInstanceManager.access$900(ReactInstanceManager.java:117)
at com.facebook.react.ReactInstanceManager$5.run(ReactInstanceManager.java:916)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Intrinsics" on path: DexPathList[[zip file "/data/app/com.xxx/base.apk"],nativeLibraryDirectories=[/data/app/com.xxx/lib/x86, /data/app/com.xxx/base.apk!/lib/x86, /system/lib, /vendor/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)

My settings.gradle file contains this:

include ':@segment/analytics-react-native'
project(':@segment/analytics-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/@segment/analytics-react-native/android')

My app/build.gradle file contains this:


compile project(path: ':@segment/analytics-react-native', configuration: 'default')

Setting the configuration to 'default' as I would otherwise get a build-time error because the kotlin-plugin adds a null value to the candidate set AttributeDisambiguationRule (read more: gradle/gradle#6747)

I am currently using Gradle 4.4, with Gradle Build tools at 3.1.4

And as you might discern from the error stacktrace, I have multiDexEnabled

PS! I have also tried version 0.1.0-beta.0, but to no avail

Android not syncing modules

When trying to sync the android project after linking the modules needed I receive the following message:

Caused by: org.gradle.api.UnknownProjectException: Project with path ':@segment_analytics-react-native' could not be found in project ':@segment/analytics-react-native-google-analytics'.

I couldn't find a solution for this..

Android build fails with 'Couldn't follow symbolic link.' error if iOS dynamic framework was installed manually.

If you install the iOS framework manually, then this happens when you try to build Android:

> Task :app:bundleReleaseJsAndAssets FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Failed to capture snapshot of input files for task ':app:bundleReleaseJsAndAssets' property '$1' during up-to-date check.
> Could not list contents of '/Users/tallpants/Projects/somereactnativeproject/node_modules/@segment/analytics-ios/.clang-format'. Couldn't follow symbolic link.

Android build fails - Unknown command-line option '--variant' after upgrade to RN 0.59.3

After upgrade React Native version from 0.57.8 to 0.59.3. I'm not able to build an app with the following command

./gradlew assemble --variant=release

it produces error

FAILURE: Build failed with an exception.

* What went wrong:
Problem configuring task :@segment_analytics-react-native:assemble from command line.
> Unknown command-line option '--variant'.

I suppose the main change in Android build is a switch to

classpath 'com.android.tools.build:gradle:3.3.1'

from

classpath 'com.android.tools.build:gradle:3.1.4'

Any ideas what should I do to be able to use variant option again?

No type argument expected for class ReactShadowNode

Android SDK Build Tools 27.0.3 will be used.
To suppress this warning, remove "buildToolsVersion '23.0.1'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.
:@segment/analytics-react-native:preBuild UP-TO-DATE
:@segment/analytics-react-native:preDebugBuild UP-TO-DATE
:@segment/analytics-react-native:compileDebugAidl UP-TO-DATE
:@segment/analytics-react-native:compileDebugRenderscript UP-TO-DATE
:@segment/analytics-react-native:checkDebugManifest UP-TO-DATE
:@segment/analytics-react-native:generateDebugBuildConfig UP-TO-DATE
:@segment/analytics-react-native:generateDebugResValues UP-TO-DATE
:@segment/analytics-react-native:generateDebugResources UP-TO-DATE
:@segment/analytics-react-native:packageDebugResources UP-TO-DATE
:@segment/analytics-react-native:platformAttrExtractor UP-TO-DATE
:@segment/analytics-react-native:processDebugManifest UP-TO-DATE
:@segment/analytics-react-native:generateDebugRFile UP-TO-DATE
:@segment/analytics-react-native:compileDebugKotline: E:\ReactNative\deleteme\facebook-rn-sample\node_modules\@segment\analytics-react-native\android\src\main\java\com\segment\analytics\reactnative\core\RNAnalyticsPackage.kt: (35, 110): No type arguments expected for class ReactShadowNode
 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':@segment/analytics-react-native:compileDebugKotlin'.
> Compilation error. See log for more details

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 5s
10 actionable tasks: 1 executed, 9 up-to-date
Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/android-setup.html

I tried integrating Segment.io react native but got this build error for the kotlin class

track() ignores undefined properties

We ran into an annoying debugging issue where the properties sent via analytics.track(name, properties) were not showing up. It seems the lib must ignore any key that has an undefined value. I think it makes a lot more sense to provide the properties, even if the values provided are undefined, and this could be valid tracking information (also would help catch bugs).

For example:

// someKey should still show up in properties.
analytics.track('myEvent', { someKey: undefined })

Android Builds failing in RN 0.59

Since upgrading to the recent React Native 0.59, my builds are failing because of this package:

FAILURE: Build failed with an exception.

* What went wrong:
The Android Gradle plugin supports only Kotlin Gradle plugin version 1.3.0 and higher.
The following dependencies do not satisfy the required version:
project ':@segment_analytics-react-native' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.60```

Middleware data never sent on Android

Example

import SegmentAnalytics from '@segment/analytics-react-native';

SegmentAnalytics.middleware(({ next, context }) => next({
    ...context,
    anyData: true,
}));

SegmentAnalytics.track(...) // -> context.anyData is sent on iOS but not on Android

Android Build failing

Unable to find method 'org.gradle.api.artifacts.dsl.DependencyHandler.getAttributesSchema()Lorg/gradle/api/attributes/AttributesSchema;'.
Possible causes for this unexpected error include:

  • Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
    Re-download dependencies and sync project (requires network)
  • The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.
    Stop Gradle build processes (requires restart)
  • Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.
In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

How to record traits without userId? (typescript)

In analytics.d.ts, identify() requires user(userId) parameter.

identify(user: string, traits?: JsonMap): Promise<void>;

But docs tells userId can be omitted if wanna just record traits.

https://segment.com/docs/sources/mobile/react-native/#identify

userId | The database ID for this user. If you donโ€™t know who the user is yet, you can omit theย userIdย and just recordย traits. You can read more in theย identify reference.

How can I omit userId when call identify() in typescript?

TypeError: Cannot read property 'wrapper' of null

Hello, I have installed the plugin to my project in the following way:
$ yarn add @ segment / analytics-react-native
$ yarn react-native link

Then I use it in the following way in a sagas action:
yield call (analytics.setup, 'writeKey', {
ย ย ย ย ย ย ย ย recordScreenViews: true,
ย ย ย ย ย ย ย ย trackAppLifecycleEvents: true,
ย ย ย ย ย ย ย ย trackAttributionData: true,

ย ย ย ย ย ย ย ย android: {
ย ย ย ย ย ย ย ย ย ย ย ย flushInterval: 60,
ย ย ย ย ย ย ย ย ย ย ย ย collectDeviceId: true
ย ย ย ย ย ย ย ย },
ย ย ย ย ย ย ย ย ios: {
ย ย ย ย ย ย ย ย ย ย ย ย trackAdvertising: true,
ย ย ย ย ย ย ย ย ย ย ย ย trackDeepLinks: true
ย ย ย ย ย ย ย ย }
ย ย ย ย })

The project connects and metric appear in Segment, but the following error appears in the console:
TypeError: Can not read property 'wrapper' of null
ย ย ย ย at 3446d8b3-78a5-4600-9 ... 3180d9c8ba34: 249698
ย ย ย ย at step (3446d8b3-78a5-4600-9 ... 3180d9c8ba34: 249615)
ย ย ย ย at Object.next (3446d8b3-78a5-4600-9 ... 3180d9c8ba34: 249545)
ย ย ย ย at fulfilled (3446d8b3-78a5-4600-9 ... 3180d9c8ba34: 249497)
ย ย ย ย at tryCallOne (3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 13419)
ย ย ย ย at 3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 13520
ย ย ย ย at 3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 14840
ย ย ย ย at _callTimer (3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 14729)
ย ย ย ย at _callImmediatesPass (3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 14765)
ย ย ย ย at Object.callImmediates (3446d8b3-78a5-4600-9 ... -3180d9c8ba34: 14984)

Can you help me solve this problem?

Thank you,
Greetings.

Android "Duplicate analytics client created"

This is a common issue on segment libraries for Android. The issue is that when you have already initialized the segment module on Android you cannot initialize it again without the app crashing.

This happens a lot when doing React Native development on Android, as running the Reload function (shake your device and press Reload) will cause this crash.

One solution is to guard the initialization on Android. While the doc of this project says you can catch errors when setting up, it doesn't seem to be working right now.

Here are some related issues in other repos with the same error:
https://github.com/leoilab/react-native-analytics-segment-io/issues/3
segmentio/analytics-android#538

Connection leaked on Android

Getting: A connection to https://api.segment.io/ was leaked. Did you forget to close a response body? on all our Android devices

We use version: 0.1.2

Any idea? Would upgrade solve it?

Didn't find class "com.adjust.sdk.Adjust" on Android

I'm having an issue with analytics-react-native-adjust:0.0.1-beta.3. The build runs fine on Android but upon launch the app crashes with the error below. The same error happens for the Amplitude module. I've added these modules using yarn add and used yarn react-native link after that. This same package/integration works well on iOS. Are we supposed to setup Adjust and Amplitude SDKs manually on Android? (this doesn't seem to be necessary on iOS)

java.lang.NoClassDefFoundError: Failed resolution of: Lcom/adjust/sdk/Adjust;
        at com.segment.analytics.android.integrations.adjust.AdjustIntegration.<init>(AdjustIntegration.java:52)
        at com.segment.analytics.android.integrations.adjust.AdjustIntegration$1.create(AdjustIntegration.java:39)
        at com.segment.analytics.Analytics.performInitializeIntegrations(Analytics.java:1460)
        at com.segment.analytics.Analytics$2$1.run(Analytics.java:279)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:440)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "com.adjust.sdk.Adjust" on path: DexPathList[[zip file "/data/app/com.hofr-y4tWHkTk7RwzIfMT6CNW7A==/base.apk"],nativeLibraryDirectories=[/data/app/com.hofr-y4tWHkTk7RwzIfMT6CNW7A==/lib/arm, /data/app/com.hofr-y4tWHkTk7RwzIfMT6CNW7A==/base.apk!/lib/armeabi-v7a, /system/lib, /system/vendor/lib]]

Sending Segment Anonymous ID to the Branch SDK

Hi folks,

Thank you for making this lib, glad to see an official React Native lib from the Segment team ๐Ÿ˜„ !

I have previously used leoilab/react-native-analytics-segment-io, which seemed to be the leading RN implementation of Segment for a while.

Has anyone had success sending the Segment Anonymous ID to the Branch SDK using this library? Or is that not possible since the Segment SDK is set up on the JavaScript side of things?

The documentation on the Branch website is a little sparse/unclear for React Native, but essentially the branch sdk needs the user's anonymous id before the branch sdk is set up, and I am not sure if that is possible using this library. The branch side of things is set up on the native end, while it seems Segment is set up on the JS side of things. As such, I am not sure if it's possible to get the value for [[SEGAnalytics sharedAnalytics] getAnonymousId] while using this implementation.

Here are the relevant excerpts:

iOS:
Inside didFinishLaunchingWithOptions

Branch *branch = [Branch getInstance];
[[Branch getInstance] setRequestMetadataKey:@"$segment_anonymous_id" value:[[SEGAnalytics sharedAnalytics] getAnonymousId]];

Android:
Before you initialize in your Application#onCreate or Deep Link Activity's #onCreate.

Branch.getInstance().setRequestMetadata("$segment_anonymous_id", com.segment.analytics.Analytics.with(this).getAnalyticsContext().traits().anonymousId());
...
Branch.initSession(...);

In the above snippet, this is the Activity context.

I opened an issue in the react-native-branch-deep-linking repo where I go into a little more detail, but wanted to post here since it's obviously related to this library.

Thank you in advance !

"Failed to load Analytics native module" error with using iOS manually

Hi there!

We tried to install segment through manually install (don't use CocoaPods), passed all steps described in readme and build was launched.

But when I try to import analytics lib import analytics from '@segment/analytics-react-native';
I got an error: Failed to load Analytics native module'
Error was reproduced on 2 environments.

I also checked integration in xcode:

  • Embedded Binaries (added Analytics.framework)
  • Embed Frameworks
    Versions: RN 0.55, analytics-ios SDK 3.7.0-beta.2

[Types] `Integrations` is non-optional in middlewares

Since #81 the integrations param is required by typescript, because these are not specified as optional in the middleware ts definitions at, e.g., https://github.com/segmentio/analytics-react-native/pull/81/files#diff-c2fd41df858e49ef9f7830528fa9a2e6R20 . Whereas the track, identify, etc methods specify a default value, making this field optional ( https://github.com/segmentio/analytics-react-native/pull/81/files#diff-c2fd41df858e49ef9f7830528fa9a2e6R20 ).

This results in any projects with custom middleware to go through necessary migration when upgrading segment to >1.1.x for typescript users that is quite confusing, as the documentation for middlewares is very sparse, and the best documentation of how to specify the integrations map is in that PR itself.

Given that the native getOptions implementation marks these as optional, there should be no challenge to providing a default or undefined value in the JS middleware definition.

Ios seems to use location by default with some integrations

Hi,
I'm using Segment with Appsflyer and Amplitude and Itunes complains that I use some location APIs without specifying the needed keys in info.plist.

My app doesn't need location at all so I'm afraid that adding that will result in a rejection.
Is there any way to disable it for the integrations?

Thank you ๐Ÿค—

 await Analytics.setup(configuration.apiKeys.segmentio, {
            using: [Amplitude, AppsFlyer],

Use @segment/analytics-react-native-appsflyer

I am trying to use the AppsFlyer integration in our project.
I have this

import analytics from '@segment/analytics-react-native';
import AppsFlyer from '@segment/analytics-react-native-appsflyer';
...
...
analytics
    .setup('MY_KEY', {
      using: [AppsFlyer],
      recordScreenViews: true,
      trackAppLifecycleEvents: true,
      trackAttributionData: true,
      ios: {
        trackAdvertising: true,
        trackDeepLinks: true
      },
      android: {
        flushInterval: 60,
        collectDeviceId: true
      }
    })

I also added pod 'RNAnalyticsIntegration-AppsFlyer', :path => '../node_modules/@segment/analytics-react-native-appsflyer' in the Podfile since it was throwing the error Failed to load Appsflyer integration native module

When I go to the segment debugger I am not able to see the integration in the payload. Is there something else I am missing?

context.device.type is not collected and it's required for Kochava according to docs

In Kochava destination docs (https://segment.com/docs/destinations/kochava/#getting-started) it says context.device.type (has value of โ€˜iosโ€™ or โ€˜androidโ€™), context.device.advertising_id (IDFA on iOS and adID on Android) and context.device.id are required in all calls to Kochava. Then in https://segment.com/docs/spec/common/#context-fields-automatically-collected it says that device.type is populated automatically by iOS library. However looking at the request data sent to Segment, context.device object only has manufacturer, model, id, advertisingId, adTrackingEnabled fields, and looking at the code of the iOS SDK https://github.com/segmentio/analytics-ios/blob/master/Analytics/Classes/Internal/SEGSegmentIntegration.m#L162 the field type doesnโ€™t exist. Is this field still required for Kochava calls or are the docs outdated?

Branch package not correct typing

Hi, Thanks for this great lib!

I noticed the Branch package isn't the right typing, I get the following error with my code:

analytics
  .configure()
    .using(Branch)
    .recordScreenViews()
    .trackAppLifecycleEvents()
    .trackAttributionData()
    .ios()
      .trackAdvertising()
      .trackDeepLinks()
    .android()
      .disableDeviceId()
  .setup('...')
  .then(() => {})
  .catch(() => {});
Argument of type '{ disabled: true; } | (() => void)' is not assignable to
parameter of type 'Integration'. Type '() => void' is not assignable to
type '    Integration'. Type '() => void' is not assignable
to type '{ disabled: true; }'. Property 'disabled' is missing in type '() => void'.

I noticed in the @segment/analytics-react-native-branch/index.d.ts file that the branch integration is declared like this:

declare const BranchIntegration:
    | {disabled: true}
    | (() => void)

Maybe this should be of the Integration type to make the typings add up?

A workaround for now is to cast Branch to any (.using(Branch as any)).

Firebase integration

Hey there!

I have one configuration question I did not know where to ask, so feel free to close this if you guys think this is not the place.

Currently integration segment with a react-native app and curious about how segment is integrating this with firebase. I mean, I am adding @segment/analytics-react-native-firebase into the project and configuring it with .using(firebase).

But more than that, do I need to follow the normal integration of firebase with native apps: https://segment.com/docs/destinations/firebase/

Or how is that plugin sending the information to firebase?

Configuring firebase as a destination at the segment page does not ask me for any firebase key for integrating it, so I guess the app talks directly with firebase and not with segment.

Thank you in advance.
Best.

Intercom Integration

Hi, I'd like to do an integration with Intercom on react-native using @segment/analytics-react-native-intercom but it seems that package lacks documentation about how to do it.

Could someone help me figure out this, please?

Thanks in advance.

Need a way to get IDFA / Ad ID / Segment Anon ID from the RN Client

Customers that use our personalization API with Personas need to be able to access the anon identifiers - this is really difficult to do right now. In the case of IDFA this means loading another third party library with additional native libraries which customers do not like to do. For our anon id, I cannot see a way to retrieve that via the RN API which is also going to be an issue for customers.

Firebase-analytics integration clashes with other firebase deps

The issue

Adding implementation(project(':@segment_analytics-react-native-firebase')) to my app build.gradle causes android builds to fail with

* What went wrong:
Execution failed for task ':app:lintVitalBetaRelease'.
> Failed to notify dependency resolution listener.
   > The library com.google.android.gms:play-services-measurement-api is being requested by various other libraries at [[16.0.4,16.0.4]], but resolves to 16.0.1. Disable the plugin and check your dependencies tree using ./gradlew :app:dependencies.
   > The library com.google.firebase:firebase-analytics is being requested by various other libraries at [[16.0.6,16.0.6]], but resolves to 16.0.3. Disable the plugin and check your dependencies tree using ./gradlew :app:dependencies.

Environment

The relevant parts of my build.gradle are

app dependencies

    implementation project(':react-native-firebase')
    implementation("com.google.firebase:firebase-core:16.0.6")
    implementation "com.google.android.gms:play-services-base:16.0.1"
    implementation "com.google.firebase:firebase-messaging:17.3.4"
    implementation "com.google.firebase:firebase-perf:16.2.3"

project dependencies

        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.google.gms:google-services:4.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
        classpath 'com.google.firebase:firebase-plugins:1.1.5'

Running gradlew :app:dependencies produces the output (trimmed for cleanliness)

+--- project :@segment_analytics-react-native
|    +--- com.segment.analytics.android:analytics:4.+ -> 4.4.0-beta1
|    |    \--- com.android.support:support-annotations:27.1.1
|    +--- com.facebook.react:react-native:+ -> 0.57.5 (*)
|    \--- org.jetbrains.kotlin:kotlin-stdlib:1.3.21
|         +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21
|         \--- org.jetbrains:annotations:13.0
+--- project :@segment_analytics-react-native-firebase
|    +--- project :@segment_analytics-react-native (*)
|    \--- com.segment.analytics.android.integrations:firebase:+ -> 1.2.0
|         +--- com.google.firebase:firebase-core:16.0.3 -> 16.0.6 (*)
|         \--- com.segment.analytics.android:analytics:4.2.6 -> 4.4.0-beta1 (*)
...
+--- project :react-native-firebase
|    +--- com.facebook.react:react-native:+ -> 0.57.5 (*)
|    +--- com.android.support:support-v4:27.1.1
|    |    +--- com.android.support:support-compat:27.1.1 (*)
|    |    +--- com.android.support:support-media-compat:27.1.1
|    |    |    +--- com.android.support:support-annotations:27.1.1
|    |    |    \--- com.android.support:support-compat:27.1.1 (*)
|    |    +--- com.android.support:support-core-utils:27.1.1 (*)
|    |    +--- com.android.support:support-core-ui:27.1.1 (*)
|    |    \--- com.android.support:support-fragment:27.1.1 (*)
|    \--- com.android.support:multidex:1.0.2
+--- com.google.firebase:firebase-core:16.0.6
|    +--- com.google.firebase:firebase-analytics:16.0.6
|    |    +--- com.google.android.gms:play-services-basement:16.0.1
|    |    |    \--- com.android.support:support-v4:26.1.0 -> 27.1.1 (*)
|    |    +--- com.google.android.gms:play-services-measurement-api:[16.0.4] -> 16.0.4
|    |    |    +--- com.google.android.gms:play-services-ads-identifier:16.0.0
|    |    |    |    \--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.android.gms:play-services-measurement-base:[16.0.5] -> 16.0.5
|    |    |    |    \--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.android.gms:play-services-stats:16.0.1
|    |    |    |    \--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.android.gms:play-services-tasks:16.0.1
|    |    |    |    \--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.firebase:firebase-analytics-impl:[16.2.4] -> 16.2.4
|    |    |    |    +--- com.google.android.gms:play-services-ads-identifier:16.0.0 (*)
|    |    |    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    |    +--- com.google.android.gms:play-services-measurement-base:[16.0.5] -> 16.0.5 (*)
|    |    |    |    +--- com.google.android.gms:play-services-stats:16.0.1 (*)
|    |    |    |    +--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    |    |    +--- com.google.firebase:firebase-common:16.0.3
|    |    |    |    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    |    |    \--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    |    |    \--- com.google.firebase:firebase-iid:17.0.3 -> 17.0.4
|    |    |    |         +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    |         +--- com.google.android.gms:play-services-stats:16.0.1 (*)
|    |    |    |         +--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    |    |         +--- com.google.firebase:firebase-common:16.0.3 (*)
|    |    |    |         \--- com.google.firebase:firebase-iid-interop:16.0.1
|    |    |    |              +--- com.google.android.gms:play-services-base:16.0.1
|    |    |    |              |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    |              |    \--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    |    |              \--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.firebase:firebase-common:16.0.3 (*)
|    |    |    \--- com.google.firebase:firebase-iid:17.0.3 -> 17.0.4 (*)
|    |    +--- com.google.android.gms:play-services-measurement-base:[16.0.5] -> 16.0.5 (*)
|    |    +--- com.google.android.gms:play-services-stats:16.0.1 (*)
|    |    +--- com.google.firebase:firebase-analytics-impl:[16.2.4] -> 16.2.4 (*)
|    |    \--- com.google.firebase:firebase-common:16.0.3 (*)
|    \--- com.google.firebase:firebase-measurement-connector-impl:17.0.4
|         +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|         +--- com.google.android.gms:play-services-measurement-api:[16.0.4] -> 16.0.4 (*)
|         +--- com.google.android.gms:play-services-measurement-base:[16.0.5] -> 16.0.5 (*)
|         +--- com.google.firebase:firebase-analytics:[16.0.6] -> 16.0.6 (*)
|         +--- com.google.firebase:firebase-analytics-impl:[16.2.4] -> 16.2.4 (*)
|         +--- com.google.firebase:firebase-common:16.0.3 (*)
|         \--- com.google.firebase:firebase-measurement-connector:17.0.1
|              \--- com.google.android.gms:play-services-basement:16.0.1 (*)
+--- com.google.android.gms:play-services-base:16.0.1 (*)
+--- com.google.firebase:firebase-messaging:17.3.4
|    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    +--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    +--- com.google.firebase:firebase-common:16.0.3 (*)
|    +--- com.google.firebase:firebase-iid:[17.0.4] -> 17.0.4 (*)
|    \--- com.google.firebase:firebase-measurement-connector:17.0.1 (*)
+--- com.google.firebase:firebase-perf:16.2.3
|    +--- com.google.android.gms:play-services-base:16.0.1 (*)
|    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    +--- com.google.android.gms:play-services-clearcut:16.0.0
|    |    +--- com.google.android.gms:play-services-base:16.0.1 (*)
|    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    +--- com.google.android.gms:play-services-phenotype:16.0.0
|    |    |    +--- com.google.android.gms:play-services-base:16.0.1 (*)
|    |    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    \--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    \--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    +--- com.google.android.gms:play-services-phenotype:16.0.0 (*)
|    +--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    +--- com.google.firebase:firebase-analytics:16.0.6 (*)
|    +--- com.google.firebase:firebase-common:16.0.3 (*)
|    +--- com.google.firebase:firebase-config:16.1.2
|    |    +--- com.google.android.gms:play-services-base:16.0.1 (*)
|    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    +--- com.google.android.gms:play-services-phenotype:16.0.0 (*)
|    |    +--- com.google.android.gms:play-services-tasks:16.0.1 (*)
|    |    +--- com.google.firebase:firebase-abt:16.0.1
|    |    |    +--- com.google.android.gms:play-services-basement:16.0.1 (*)
|    |    |    +--- com.google.firebase:firebase-analytics-impl:16.2.2 -> 16.2.4 (*)
|    |    |    \--- com.google.firebase:firebase-common:16.0.3 (*)
|    |    +--- com.google.firebase:firebase-analytics:16.0.6 (*)
|    |    +--- com.google.firebase:firebase-analytics-impl:16.2.4 (*)
|    |    +--- com.google.firebase:firebase-common:16.0.3 (*)
|    |    \--- com.google.firebase:firebase-iid:17.0.3 -> 17.0.4 (*)
|    \--- com.google.firebase:firebase-iid:17.0.3 -> 17.0.4 (*)

Misc

I've tried doing

    implementation("com.google.firebase:firebase-core:16.0.6") {
        force = true
    }

    implementation(project(':@segment_analytics-react-native-firebase')) {
        transitive = false
    }

and

    implementation(project(':@segment_analytics-react-native-firebase')) {
        exclude group "com.google.firebase"
    }

with no effect.

It seems like segment_analytics-react-native-firebase has its own dependency on 'com.segment.analytics.android.integrations:firebase:+@aar', and that module has a dependency on Firebase analytics 16.0.3 which for some reason is being respected.

AnonymousId support

I'm using @segment/analytics-react-native - ^0.0.1-beta.3 in order to replicate some functionality in a new React Native app. Based on the documentation, it looks like the iOS SDK has a getAnonymousId method, but this project does not. Is it possible to access the anonymousId using analytics-react-native? I'm trying to prevent multiple identify calls if a user has already been identified.

TypeScript error in bridge.d.ts and analytics.d.ts

Hi,

I recently stumbled upon the following error when running tsc in my project:

node_modules/@segment/analytics-react-native/build/esm/analytics.d.ts:89:26 - error TS1039: Initializers are not allowed in ambient contexts.

89         readonly ready = false;
                            ~~~~~

node_modules/@segment/analytics-react-native/build/esm/bridge.d.ts:2:17 - error TS2503: Cannot find namespace 'NativeModules'.

2 import bridge = NativeModules.RNAnalytics;
                  ~~~~~~~~~~~~~

node_modules/@segment/analytics-react-native/build/esm/bridge.d.ts:4:31 - error TS2503: Cannot find namespace 'NativeModules'.

4 export declare type JsonMap = NativeModules.RNAnalytics.JsonMap;

This doesn't cause any errors in the app as far as I can see.

RNAnalytics not found

I am consistently getting a @segment/analytics-react-native-mixpanel/ios/main.m:10:9: 'RNAnalytics/RNAnalytics.h' file not foundwhen adding the mixpanel integration. I am currently integratitng the analytics-react-native library without cocoapods.

I have followed the steps in the readme and #17, running the project without the mixpanel integration will correctly send data to segment but not to mixpanel.

Android lifecycle events not captured

We are using @segment/analytics-react-native - ^0.0.1-beta.3 in our application. But the Lifecycle events are not being tracked automatically in the case of Android, though it works fine in iOS. We tried to simulate the same using a new RN project and facing the same issue with Android.

"dependencies": {
    "@segment/analytics-ios": "github:segmentio/analytics-ios#3.6.10",
    "@segment/analytics-react-native": "^0.0.1-beta.3",
    "react": "16.6.3",
    "react-native": "0.58.3",
    "react-native-gesture-handler": "^1.0.15"
  },
  "devDependencies": {
    "babel-core": "^7.0.0-bridge.0",
    "babel-jest": "24.0.0",
    "jest": "24.0.0",
    "metro-react-native-babel-preset": "0.51.1",
    "react-test-renderer": "16.6.3"
  }

Test Repo:

https://github.com/anandwahed/test-rn-segmentsdk

_analyticsReactNative.default.configure is not a function

I'm trying to follow the official doc but always i get this error.
this is my code :

//Analytics
import analytics from '@segment/analytics-react-native';

async componentDidMount(){
        try {
            await analytics
                .configure()
                .recordScreenViews() // Record screen views automatically!
                .trackAppLifecycleEvents() // Record certain application events automatically!
                .setup(constant.API_KEY_SEGMENTS);
            console.log('Analytics Segments', analytics);
        } catch (error) {
            console.log('Analytics Segments', error);
        }
    }

this is the error

Analytics Segments TypeError: _analyticsReactNative.default.configure is not a function
    at App.componentDidMount$ (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:81671:79)
    at tryCatch (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:20957:19)
    at Generator.invoke [as _invoke] (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21130:24)
    at Generator.prototype.(anonymous function) [as next] (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21000:23)
    at tryCatch (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:20957:19)
    at invoke (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21033:22)
    at blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21061:13
    at tryCallTwo (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:20295:7)
    at doResolve (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:20459:15)
    at new Promise (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:20318:5)
    at callInvokeWithMethodAndArg (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21060:18)
    at AsyncIterator.enqueue [as _invoke] (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21065:131)
    at AsyncIterator.prototype.(anonymous function) [as next] (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21000:23)
    at Object.runtime.async (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:21081:65)
    at App.componentDidMount (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:81665:35)
    at App.proxiedComponentDidMount (blob:http://localhost:8081/b0fb13ef-0be5-4e34-bcac-984d38d82f8b:38920:42)

fatal error: 'Analytics/SEGAnalytics.h' file not found

I did the following:

  1. yarn add @segment/analytics-react-native
  2. yarn react-native link
  3. cd ios
  4. pod init
  5. vim Podfile and make changes so that my podfile looks like this:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'my_app' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for my_app
  pod 'React', :path => '../node_modules/react-native'
  pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
  pod 'RNAnalytics', :path => '../node_modules/@segment/analytics-react-native'

  target 'my_app-tvOSTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'my_appTests' do
    inherit! :search_paths
    # Pods for testing
  end

end
  1. pod install
  2. cd .. && react-native run-ios
    i am on react-native 0.58.6

I am greeted with

10:9: fatal error: 'Analytics/SEGAnalytics.h' file not found
#import <Analytics/SEGAnalytics.h>

Can't I use duplicated client from RN and native Segment?

Hi, I've been using the previous module of this, react-native-analytics-segment-io which is outdated.
And at the same time. I'm using Segment on the native side too with same writeKey and it worked fine.

But since I'm moving to this new module.
It doesn't seem to work as I had done before.

  1. I got a warning when I do init(setup) from both JS and Android native side

Duplicate analytics client created with tag:

And tracking call from JS didn't reach to Segment.

  1. It doesn't send tracking when I don't init(setup) from JS

  2. iOS seems to be working. (when I do setup on both side)

Any help?

Build 1.0.0 Broken - @segment/analytics-react-native

Just updated to 1.0.0 and yarn install.

Seems the package from npm has source but not the built part of the library.

-rw-r--r--   1 jake  staff  14267 Jun  6 11:14 README.md
-rw-r--r--   1 jake  staff    904 Jun  6 11:14 RNAnalytics.podspec
drwxr-xr-x   4 jake  staff    128 Jun  6 11:25 android
drwxr-xr-x   4 jake  staff    128 Jun  6 11:25 ios
-rw-r--r--   1 jake  staff   1957 Jun  6 11:14 package.json
drwxr-xr-x  13 jake  staff    416 Jun  6 11:25 src

Offline-first support

Does this lib support buffer events when the device is offline? if yes, maybe I could update the docs?

Is this repo being maintained?

I'm switching from react-native-analytics-segment-io to this since I see some problem to get old one work with latest react-native and it's official one(thanks).

So, I want to know this one is working well (especially with latest react-native) and it's being maintained well. I saw some issues that didn't get a response by Segment.

Many people are using this and it's Ok?

The Android Gradle plugin supports only Kotlin Gradle plugin version 1.3.10 and higher. The following dependencies do not satisfy the required version: project ':@segment_analytics-react-native-firebase' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0

I couldn't find the repo for segment_analytics-react-native-firebase for opening here.

Environment:
gradle:3.4.1
"@segment/analytics-react-native": "^1.1.0-beta.0",
"@segment/analytics-react-native-firebase": "^1.1.0-beta.0",
"react": "16.8.6",
"react-native": "0.60.4",

I'm getting the following error

The Android Gradle plugin supports only Kotlin Gradle plugin version 1.3.10 and higher. The following dependencies do not satisfy the required version: project ':@segment_analytics-react-native-firebase' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0

Then I opened
@segment > analytics-react-native-firebase > build.gradle and I saw

classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.0'

which is supposed to be as same in @segment/analytics-react-native which is

classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.21'

I can't make a PR, so temporarily changing in node_modules and expect a patch to publish soon.

Is recordScreenViews flag working?

I don't see it work for me on Android. We are using react-native-navigation. Is it fundamentally different from Android screens so that Segment cannot record screen automatically?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.