Skip to content

Commit 15c050c

Browse files
Add app install, foreground and background event and application entity tracking (#1396)
1 parent d6a87a0 commit 15c050c

20 files changed

+648
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@snowplow/react-native-tracker](./react-native-tracker.md) &gt; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) &gt; [appBuild](./react-native-tracker.applifecycleconfiguration.appbuild.md)
4+
5+
## AppLifecycleConfiguration.appBuild property
6+
7+
Build name of the application e.g s9f2k2d or 1.1.0 beta
8+
9+
Entity schema: `iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0`
10+
11+
<b>Signature:</b>
12+
13+
```typescript
14+
appBuild?: string;
15+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@snowplow/react-native-tracker](./react-native-tracker.md) &gt; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) &gt; [appVersion](./react-native-tracker.applifecycleconfiguration.appversion.md)
4+
5+
## AppLifecycleConfiguration.appVersion property
6+
7+
Version number of the application e.g 1.1.0 (semver or git commit hash).
8+
9+
Entity schema if `appBuild` property is set: `iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0` Entity schema if `appBuild` property is not set: `iglu:com.snowplowanalytics.snowplow/application/jsonschema/1-0-0`
10+
11+
<b>Signature:</b>
12+
13+
```typescript
14+
appVersion?: string;
15+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@snowplow/react-native-tracker](./react-native-tracker.md) &gt; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) &gt; [installAutotracking](./react-native-tracker.applifecycleconfiguration.installautotracking.md)
4+
5+
## AppLifecycleConfiguration.installAutotracking property
6+
7+
Whether to automatically track app install event on first run.
8+
9+
Schema: `iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0`
10+
11+
<b>Signature:</b>
12+
13+
```typescript
14+
installAutotracking?: boolean;
15+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@snowplow/react-native-tracker](./react-native-tracker.md) &gt; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) &gt; [lifecycleAutotracking](./react-native-tracker.applifecycleconfiguration.lifecycleautotracking.md)
4+
5+
## AppLifecycleConfiguration.lifecycleAutotracking property
6+
7+
Whether to automatically track app lifecycle events (app foreground and background events). Also adds a lifecycle context entity to all events.
8+
9+
Foreground event schema: `iglu:com.snowplowanalytics.snowplow/application_foreground/jsonschema/1-0-0` Background event schema: `iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0` Context entity schema: `iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0`
10+
11+
<b>Signature:</b>
12+
13+
```typescript
14+
lifecycleAutotracking?: boolean;
15+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [@snowplow/react-native-tracker](./react-native-tracker.md) &gt; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md)
4+
5+
## AppLifecycleConfiguration interface
6+
7+
Configuration for app lifecycle tracking
8+
9+
<b>Signature:</b>
10+
11+
```typescript
12+
export interface AppLifecycleConfiguration
13+
```
14+
15+
## Properties
16+
17+
| Property | Type | Description |
18+
| --- | --- | --- |
19+
| [appBuild?](./react-native-tracker.applifecycleconfiguration.appbuild.md) | string | <i>(Optional)</i> Build name of the application e.g s9f2k2d or 1.1.0 beta<!-- -->Entity schema: <code>iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0</code> |
20+
| [appVersion?](./react-native-tracker.applifecycleconfiguration.appversion.md) | string | <i>(Optional)</i> Version number of the application e.g 1.1.0 (semver or git commit hash).<!-- -->Entity schema if <code>appBuild</code> property is set: <code>iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0</code> Entity schema if <code>appBuild</code> property is not set: <code>iglu:com.snowplowanalytics.snowplow/application/jsonschema/1-0-0</code> |
21+
| [installAutotracking?](./react-native-tracker.applifecycleconfiguration.installautotracking.md) | boolean | <i>(Optional)</i> Whether to automatically track app install event on first run.<!-- -->Schema: <code>iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0</code> |
22+
| [lifecycleAutotracking?](./react-native-tracker.applifecycleconfiguration.lifecycleautotracking.md) | boolean | <i>(Optional)</i> Whether to automatically track app lifecycle events (app foreground and background events). Also adds a lifecycle context entity to all events.<!-- -->Foreground event schema: <code>iglu:com.snowplowanalytics.snowplow/application_foreground/jsonschema/1-0-0</code> Background event schema: <code>iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0</code> Context entity schema: <code>iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0</code> |
23+

api-docs/docs/react-native-tracker/markdown/react-native-tracker.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
| Interface | Description |
2626
| --- | --- |
27+
| [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) | Configuration for app lifecycle tracking |
2728
| [CoreConfiguration](./react-native-tracker.coreconfiguration.md) | The configuration object for the tracker core library |
2829
| [CorePlugin](./react-native-tracker.coreplugin.md) | Interface which defines Core Plugins |
2930
| [CorePluginConfiguration](./react-native-tracker.corepluginconfiguration.md) | The configuration of the plugin to add |

api-docs/docs/react-native-tracker/markdown/react-native-tracker.newtracker.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ Creates a new tracker instance with the given configuration
99
<b>Signature:</b>
1010

1111
```typescript
12-
export declare function newTracker(configuration: TrackerConfiguration & EmitterConfiguration & SessionConfiguration & SubjectConfiguration & EventStoreConfiguration & ScreenTrackingConfiguration & PlatformContextConfiguration & DeepLinkConfiguration): Promise<ReactNativeTracker>;
12+
export declare function newTracker(configuration: TrackerConfiguration & EmitterConfiguration & SessionConfiguration & SubjectConfiguration & EventStoreConfiguration & ScreenTrackingConfiguration & PlatformContextConfiguration & DeepLinkConfiguration & AppLifecycleConfiguration): Promise<ReactNativeTracker>;
1313
```
1414

1515
## Parameters
1616

1717
| Parameter | Type | Description |
1818
| --- | --- | --- |
19-
| configuration | [TrackerConfiguration](./react-native-tracker.trackerconfiguration.md) &amp; EmitterConfiguration &amp; [SessionConfiguration](./react-native-tracker.sessionconfiguration.md) &amp; [SubjectConfiguration](./react-native-tracker.subjectconfiguration.md) &amp; [EventStoreConfiguration](./react-native-tracker.eventstoreconfiguration.md) &amp; ScreenTrackingConfiguration &amp; [PlatformContextConfiguration](./react-native-tracker.platformcontextconfiguration.md) &amp; [DeepLinkConfiguration](./react-native-tracker.deeplinkconfiguration.md) | Configuration for the tracker |
19+
| configuration | [TrackerConfiguration](./react-native-tracker.trackerconfiguration.md) &amp; EmitterConfiguration &amp; [SessionConfiguration](./react-native-tracker.sessionconfiguration.md) &amp; [SubjectConfiguration](./react-native-tracker.subjectconfiguration.md) &amp; [EventStoreConfiguration](./react-native-tracker.eventstoreconfiguration.md) &amp; ScreenTrackingConfiguration &amp; [PlatformContextConfiguration](./react-native-tracker.platformcontextconfiguration.md) &amp; [DeepLinkConfiguration](./react-native-tracker.deeplinkconfiguration.md) &amp; [AppLifecycleConfiguration](./react-native-tracker.applifecycleconfiguration.md) | Configuration for the tracker |
2020

2121
<b>Returns:</b>
2222

api-docs/docs/react-native-tracker/markdown/react-native-tracker.reactnativetracker.md

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export declare type ReactNativeTracker = {
4242
readonly getSessionId: () => Promise<string | undefined>;
4343
readonly getSessionIndex: () => Promise<number | undefined>;
4444
readonly getSessionState: () => Promise<SessionState | undefined>;
45+
readonly getIsInBackground: () => boolean | undefined;
46+
readonly getBackgroundIndex: () => number | undefined;
47+
readonly getForegroundIndex: () => number | undefined;
4548
readonly enablePlatformContext: () => Promise<void>;
4649
readonly disablePlatformContext: () => void;
4750
readonly refreshPlatformContext: () => Promise<void>;

api-docs/docs/react-native-tracker/react-native-tracker.api.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import { BrowserPlugin } from '@snowplow/browser-tracker-core';
88
import { BrowserPluginConfiguration } from '@snowplow/browser-tracker-core';
99
import { ScreenTrackingConfiguration } from '@snowplow/browser-plugin-screen-tracking';
1010

11+
// @public
12+
export interface AppLifecycleConfiguration {
13+
appBuild?: string;
14+
appVersion?: string;
15+
installAutotracking?: boolean;
16+
lifecycleAutotracking?: boolean;
17+
}
18+
1119
// @public
1220
export type ConditionalContextProvider = FilterProvider | RuleSetProvider;
1321

@@ -260,7 +268,7 @@ export type MessageNotificationProps = {
260268
};
261269

262270
// @public
263-
export function newTracker(configuration: TrackerConfiguration & EmitterConfiguration & SessionConfiguration & SubjectConfiguration & EventStoreConfiguration & ScreenTrackingConfiguration & PlatformContextConfiguration & DeepLinkConfiguration): Promise<ReactNativeTracker>;
271+
export function newTracker(configuration: TrackerConfiguration & EmitterConfiguration & SessionConfiguration & SubjectConfiguration & EventStoreConfiguration & ScreenTrackingConfiguration & PlatformContextConfiguration & DeepLinkConfiguration & AppLifecycleConfiguration): Promise<ReactNativeTracker>;
264272

265273
// @public
266274
export interface PageViewEvent {
@@ -377,6 +385,9 @@ export type ReactNativeTracker = {
377385
readonly getSessionId: () => Promise<string | undefined>;
378386
readonly getSessionIndex: () => Promise<number | undefined>;
379387
readonly getSessionState: () => Promise<SessionState | undefined>;
388+
readonly getIsInBackground: () => boolean | undefined;
389+
readonly getBackgroundIndex: () => number | undefined;
390+
readonly getForegroundIndex: () => number | undefined;
380391
readonly enablePlatformContext: () => Promise<void>;
381392
readonly disablePlatformContext: () => void;
382393
readonly refreshPlatformContext: () => Promise<void>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@snowplow/react-native-tracker",
5+
"comment": "Add app install, foreground and background event and application entity tracking (#1396)",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@snowplow/react-native-tracker"
10+
}

trackers/react-native-tracker/src/constants.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ export const FOREGROUND_EVENT_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/appl
22
export const BACKGROUND_EVENT_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0';
33
export const DEEP_LINK_RECEIVED_EVENT_SCHEMA = 'iglu:com.snowplowanalytics.mobile/deep_link_received/jsonschema/1-0-0';
44
export const SCREEN_VIEW_EVENT_SCHEMA = 'iglu:com.snowplowanalytics.mobile/screen_view/jsonschema/1-0-0';
5+
export const APPLICATION_INSTALL_EVENT_SCHEMA = 'iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0';
56

67
export const CLIENT_SESSION_ENTITY_SCHEMA ='iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2'
78
export const MOBILE_CONTEXT_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-3';
89
export const DEEP_LINK_ENTITY_SCHEMA = 'iglu:com.snowplowanalytics.mobile/deep_link/jsonschema/1-0-0';
10+
export const LIFECYCLE_CONTEXT_SCHEMA = 'iglu:com.snowplowanalytics.mobile/application_lifecycle/jsonschema/1-0-0';
11+
export const MOBILE_APPLICATION_CONTEXT_SCHEMA = 'iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0';
12+
export const APPLICATION_CONTEXT_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/application/jsonschema/1-0-0';
913

1014
export const PAGE_URL_PROPERTY = 'url';
1115
export const PAGE_REFERRER_PROPERTY = 'refr';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { CorePluginConfiguration, SelfDescribingJson } from '@snowplow/tracker-core';
2+
import { AppLifecycleConfiguration } from '../../types';
3+
import { APPLICATION_CONTEXT_SCHEMA, MOBILE_APPLICATION_CONTEXT_SCHEMA } from '../../constants';
4+
5+
/**
6+
* Tracks the application context entity with information about the app version.
7+
* If appBuild is provided, a mobile application context is tracked, otherwise the Web equivalent is tracked.
8+
*
9+
* Entity schema if `appBuild` property is set: `iglu:com.snowplowanalytics.mobile/application/jsonschema/1-0-0`
10+
* Entity schema if `appBuild` property is not set: `iglu:com.snowplowanalytics.snowplow/application/jsonschema/1-0-0`
11+
*/
12+
export function newAppContextPlugin({ appVersion, appBuild }: AppLifecycleConfiguration): CorePluginConfiguration {
13+
const contexts = () => {
14+
let entities: SelfDescribingJson[] = [];
15+
16+
if (appVersion) {
17+
// Add application context to all events
18+
if (appBuild) {
19+
entities.push({
20+
schema: MOBILE_APPLICATION_CONTEXT_SCHEMA,
21+
data: {
22+
version: appVersion,
23+
build: appBuild,
24+
},
25+
});
26+
} else {
27+
entities.push({
28+
schema: APPLICATION_CONTEXT_SCHEMA,
29+
data: {
30+
version: appVersion,
31+
},
32+
});
33+
}
34+
}
35+
36+
return entities;
37+
};
38+
39+
return {
40+
plugin: {
41+
contexts,
42+
},
43+
};
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { buildSelfDescribingEvent, CorePluginConfiguration, TrackerCore } from '@snowplow/tracker-core';
2+
import { AppLifecycleConfiguration, TrackerConfiguration } from '../../types';
3+
import { APPLICATION_INSTALL_EVENT_SCHEMA } from '../../constants';
4+
import AsyncStorage from '@react-native-async-storage/async-storage';
5+
6+
/**
7+
* Tracks an application install event on the first run of the app.
8+
* Stores the install event in AsyncStorage to prevent tracking on subsequent runs.
9+
*
10+
* Event schema: `iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0`
11+
*/
12+
export function newAppInstallPlugin(
13+
{ namespace, installAutotracking = false }: TrackerConfiguration & AppLifecycleConfiguration,
14+
core: TrackerCore
15+
): CorePluginConfiguration {
16+
if (installAutotracking) {
17+
// Track install event on first run
18+
const key = `snowplow_${namespace}_install`;
19+
setTimeout(async () => {
20+
const installEvent = await AsyncStorage.getItem(key);
21+
if (!installEvent) {
22+
core.track(
23+
buildSelfDescribingEvent({
24+
event: {
25+
schema: APPLICATION_INSTALL_EVENT_SCHEMA,
26+
data: {},
27+
},
28+
})
29+
);
30+
await AsyncStorage.setItem(key, new Date().toISOString());
31+
}
32+
}, 0);
33+
}
34+
return {
35+
plugin: {},
36+
};
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import {
2+
buildSelfDescribingEvent,
3+
CorePluginConfiguration,
4+
SelfDescribingJson,
5+
TrackerCore,
6+
} from '@snowplow/tracker-core';
7+
import { AppLifecycleConfiguration, EventContext } from '../../types';
8+
import { BACKGROUND_EVENT_SCHEMA, FOREGROUND_EVENT_SCHEMA, LIFECYCLE_CONTEXT_SCHEMA } from '../../constants';
9+
import { AppState } from 'react-native';
10+
11+
export interface AppLifecyclePlugin extends CorePluginConfiguration {
12+
getIsInBackground: () => boolean | undefined;
13+
getBackgroundIndex: () => number | undefined;
14+
getForegroundIndex: () => number | undefined;
15+
}
16+
17+
/**
18+
* Tracks foreground and background events automatically when the app state changes.
19+
* Also adds a lifecycle context to all events with information about the app visibility.
20+
*/
21+
export async function newAppLifecyclePlugin(
22+
{ lifecycleAutotracking = true }: AppLifecycleConfiguration,
23+
core: TrackerCore
24+
): Promise<AppLifecyclePlugin> {
25+
let isInForeground = AppState.currentState !== 'background';
26+
let foregroundIndex = isInForeground ? 1 : 0;
27+
let backgroundIndex = isInForeground ? 0 : 1;
28+
let subscription: ReturnType<typeof AppState.addEventListener> | undefined;
29+
30+
if (lifecycleAutotracking) {
31+
// Subscribe to app state changes and track foreground/background events
32+
subscription = AppState.addEventListener('change', async (nextAppState) => {
33+
if (nextAppState === 'active' && !isInForeground) {
34+
trackForegroundEvent();
35+
}
36+
if (nextAppState === 'background' && isInForeground) {
37+
trackBackgroundEvent();
38+
}
39+
});
40+
}
41+
42+
const contexts = () => {
43+
let entities: SelfDescribingJson[] = [];
44+
45+
if (lifecycleAutotracking) {
46+
// Add lifecycle context to all events
47+
entities.push({
48+
schema: LIFECYCLE_CONTEXT_SCHEMA,
49+
data: {
50+
isVisible: isInForeground,
51+
index: isInForeground ? foregroundIndex : backgroundIndex,
52+
},
53+
});
54+
}
55+
56+
return entities;
57+
};
58+
59+
const deactivatePlugin = () => {
60+
if (subscription) {
61+
subscription.remove();
62+
subscription = undefined;
63+
}
64+
};
65+
66+
const trackForegroundEvent = (contexts?: EventContext[]) => {
67+
if (!isInForeground) {
68+
isInForeground = true;
69+
foregroundIndex += 1;
70+
}
71+
core.track(
72+
buildSelfDescribingEvent({ event: { schema: FOREGROUND_EVENT_SCHEMA, data: { foregroundIndex } } }),
73+
contexts
74+
);
75+
};
76+
77+
const trackBackgroundEvent = (contexts?: EventContext[]) => {
78+
if (isInForeground) {
79+
isInForeground = false;
80+
backgroundIndex += 1;
81+
}
82+
core.track(
83+
buildSelfDescribingEvent({ event: { schema: BACKGROUND_EVENT_SCHEMA, data: { backgroundIndex } } }),
84+
contexts
85+
);
86+
};
87+
88+
return {
89+
getIsInBackground: () => (lifecycleAutotracking ? !isInForeground : undefined),
90+
getBackgroundIndex: () => (lifecycleAutotracking ? backgroundIndex : undefined),
91+
getForegroundIndex: () => (lifecycleAutotracking ? foregroundIndex : undefined),
92+
plugin: {
93+
contexts,
94+
deactivatePlugin,
95+
},
96+
};
97+
}

0 commit comments

Comments
 (0)