@attryio/react-native 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -9
- package/dist/index.js +69 -9
- package/package.json +26 -2
package/README.md
CHANGED
|
@@ -1,40 +1,148 @@
|
|
|
1
1
|
# @attryio/react-native
|
|
2
2
|
|
|
3
|
-
React Native SDK for Attry mobile attribution.
|
|
3
|
+
React Native SDK for [Attry](https://attry.io), a mobile app analytics and attribution platform for campaign analytics, app user analytics, deep links, deferred deep links, and revenue events.
|
|
4
|
+
|
|
5
|
+
Use this package when your app is built with React Native. It collects install/open/session signals automatically, keeps one stable installation ID, resolves Attry deep links, and lets you send custom product events with structured properties.
|
|
6
|
+
|
|
7
|
+
## Links
|
|
8
|
+
|
|
9
|
+
- Website: [attry.io](https://attry.io)
|
|
10
|
+
- Dashboard: [app.attry.io](https://app.attry.io)
|
|
11
|
+
- Event tracking docs: [attry.io/resources/event-tracking](https://attry.io/resources/event-tracking)
|
|
12
|
+
- Standard events: [attry.io/resources/standard-events](https://attry.io/resources/standard-events)
|
|
13
|
+
- Domain setup: [attry.io/resources/domain-setup](https://attry.io/resources/domain-setup)
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @attryio/react-native
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For iOS, install pods after adding the package:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
cd ios && pod install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use the App ID and live SDK key from **Settings -> SDK install** in your Attry dashboard. Each Attry app has one App ID and one live key; you do not need separate keys for iOS and Android.
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
Create the client once during app startup.
|
|
4
32
|
|
|
5
33
|
```ts
|
|
6
34
|
import { createAttryReactNative } from "@attryio/react-native";
|
|
7
35
|
|
|
8
|
-
const attry = await createAttryReactNative({
|
|
36
|
+
export const attry = await createAttryReactNative({
|
|
9
37
|
appId: "457064853",
|
|
10
38
|
apiKey: "attry_live_..."
|
|
11
39
|
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The SDK starts with practical defaults:
|
|
43
|
+
|
|
44
|
+
- Tracks the first SDK install, app opens, session starts/ends, foreground/background changes, and deep link opens.
|
|
45
|
+
- Collects React Native context such as platform, app version, build, OS version, device model, locale, timezone, screen, and available country context.
|
|
46
|
+
- Resolves Android Google Play Install Referrer when the native module is available.
|
|
47
|
+
- Requests Apple AdServices attribution tokens on iOS when the native module is available.
|
|
48
|
+
- Batches events and retries failed requests through the shared Attry SDK engine.
|
|
49
|
+
|
|
50
|
+
## React Native compatibility
|
|
51
|
+
|
|
52
|
+
The SDK avoids importing the root `react-native` namespace at runtime. Instead, it loads only the React Native APIs it needs, which prevents deprecated core export getters from being touched in newer React Native versions.
|
|
53
|
+
|
|
54
|
+
## Track custom events
|
|
12
55
|
|
|
56
|
+
Use `track` for app-specific actions. Event names should be stable `snake_case`; details belong in `properties`.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
await attry.track("flashcards_generated", {
|
|
60
|
+
properties: {
|
|
61
|
+
deckId: "deck_123",
|
|
62
|
+
cardCount: 18,
|
|
63
|
+
source: "lesson"
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Attach a signed-in user ID with `identify`. This is a method, not an event.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
attry.identify("user_123");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Purchase intent and revenue
|
|
75
|
+
|
|
76
|
+
Use `initiatePurchase` when the user starts a checkout, subscription, or paywall purchase flow.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
13
79
|
await attry.initiatePurchase({
|
|
14
80
|
properties: {
|
|
15
81
|
productId: "pro_monthly",
|
|
16
82
|
placement: "paywall"
|
|
17
83
|
}
|
|
18
84
|
});
|
|
85
|
+
```
|
|
19
86
|
|
|
87
|
+
Use `purchase` when money was actually captured or a paid conversion happened. `purchase` must include `value` or `amountMinor` plus `currency`.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
20
90
|
await attry.purchase({
|
|
21
91
|
value: 31.42,
|
|
22
92
|
currency: "USD",
|
|
23
93
|
productId: "pro_monthly",
|
|
24
|
-
transactionId: "txn_123"
|
|
94
|
+
transactionId: "txn_123",
|
|
95
|
+
store: "app_store",
|
|
96
|
+
properties: {
|
|
97
|
+
plan: "monthly"
|
|
98
|
+
}
|
|
25
99
|
});
|
|
26
100
|
```
|
|
27
101
|
|
|
28
|
-
|
|
102
|
+
Attry stores revenue as stable minor-unit fields, so campaign revenue is not guessed from arbitrary custom properties.
|
|
29
103
|
|
|
30
104
|
## Standard events
|
|
31
105
|
|
|
32
106
|
Attry accepts custom event names, but these standard names are reserved for dashboard reporting:
|
|
33
107
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
108
|
+
| Event | Sent by | Purpose |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `install` | SDK | First SDK install for the current app installation. |
|
|
111
|
+
| `open` | SDK | App opened or became active. |
|
|
112
|
+
| `session_started` | SDK | A new app usage session started. |
|
|
113
|
+
| `session_ended` | SDK | A session ended after backgrounding or timeout. |
|
|
114
|
+
| `app_foreground` | SDK | App returned to the foreground. |
|
|
115
|
+
| `app_background` | SDK | App moved to the background. |
|
|
116
|
+
| `deep_link_opened` | SDK | App opened from a deep link, universal link, or app link. |
|
|
117
|
+
| `initiate_purchase` | App | User started a purchase or subscription flow. |
|
|
118
|
+
| `purchase` | App | Paid conversion or purchase revenue. Requires revenue. |
|
|
119
|
+
|
|
120
|
+
Everything else can be sent as a custom event with `track("your_event_name", { properties })`.
|
|
121
|
+
|
|
122
|
+
## Configuration
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const attry = await createAttryReactNative({
|
|
126
|
+
appId: "457064853",
|
|
127
|
+
apiKey: "attry_live_...",
|
|
128
|
+
autoTrackLifecycleEvents: true,
|
|
129
|
+
autoTrackDeepLinks: true,
|
|
130
|
+
autoCollectInstallAttribution: true,
|
|
131
|
+
sessionTimeoutMs: 30 * 60 * 1000,
|
|
132
|
+
context: {
|
|
133
|
+
appName: "Noteasy"
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Useful options:
|
|
139
|
+
|
|
140
|
+
- `endpoint`: custom Attry API endpoint. Defaults to `https://api.attry.io`.
|
|
141
|
+
- `autoTrackLifecycleEvents`: disable automatic lifecycle events when set to `false`.
|
|
142
|
+
- `autoTrackDeepLinks`: disable automatic deep link tracking when set to `false`.
|
|
143
|
+
- `autoCollectInstallAttribution`: disable Apple AdServices and Play Install Referrer collection when set to `false`.
|
|
144
|
+
- `context`: extra app, device, or release metadata attached to every event.
|
|
37
145
|
|
|
38
|
-
|
|
146
|
+
## Need help?
|
|
39
147
|
|
|
40
|
-
|
|
148
|
+
Email [hello@attry.io](mailto:hello@attry.io) or open the [Attry dashboard](https://app.attry.io).
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Attry, ATTRY_EVENTS, MemoryStorage, parseAttryUrl } from "@attryio/sdk-core";
|
|
2
2
|
const SDK_NAME = "attry-react-native";
|
|
3
|
-
const SDK_VERSION = "0.1.
|
|
3
|
+
const SDK_VERSION = "0.1.8";
|
|
4
4
|
export { parseAttryUrl };
|
|
5
5
|
export async function createAttryReactNative(config) {
|
|
6
|
-
const rn =
|
|
6
|
+
const rn = loadReactNative();
|
|
7
7
|
const platform = rn?.Platform?.OS === "ios" || rn?.Platform?.OS === "android"
|
|
8
8
|
? rn.Platform.OS
|
|
9
9
|
: "unknown";
|
|
@@ -30,7 +30,7 @@ export async function createAttryReactNative(config) {
|
|
|
30
30
|
return client;
|
|
31
31
|
}
|
|
32
32
|
export async function collectReactNativeContext(rn, configuredContext = {}) {
|
|
33
|
-
const native = rn ??
|
|
33
|
+
const native = rn ?? loadReactNative();
|
|
34
34
|
const modules = native?.NativeModules;
|
|
35
35
|
const deviceInfo = modules?.RNDeviceInfo;
|
|
36
36
|
const expoConstants = modules?.ExpoConstants ?? modules?.ExponentConstants;
|
|
@@ -131,7 +131,7 @@ export async function collectReactNativeContext(rn, configuredContext = {}) {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
export async function attachReactNativeLifecycleTracking(client, rn, storage = new MemoryStorage(), options) {
|
|
134
|
-
const native = rn ??
|
|
134
|
+
const native = rn ?? loadReactNative();
|
|
135
135
|
const installKey = `attry.${options.appId}.install_tracked`;
|
|
136
136
|
const sessionStartedAt = Date.now();
|
|
137
137
|
const sessionId = `ses_${sessionStartedAt.toString(36)}`;
|
|
@@ -210,7 +210,7 @@ export async function attachReactNativeLifecycleTracking(client, rn, storage = n
|
|
|
210
210
|
});
|
|
211
211
|
}
|
|
212
212
|
export async function attachDeepLinkTracking(client, rn) {
|
|
213
|
-
const native = rn ??
|
|
213
|
+
const native = rn ?? loadReactNative();
|
|
214
214
|
const initialUrl = await native?.Linking?.getInitialURL?.();
|
|
215
215
|
if (initialUrl) {
|
|
216
216
|
await trackDeepLinkOpen(client, initialUrl, "initial_url", native);
|
|
@@ -220,7 +220,7 @@ export async function attachDeepLinkTracking(client, rn) {
|
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
export async function collectInstallAttribution(client, rn) {
|
|
223
|
-
const native = rn ??
|
|
223
|
+
const native = rn ?? loadReactNative();
|
|
224
224
|
if (native?.Platform?.OS === "ios") {
|
|
225
225
|
const token = await native.NativeModules?.AttryAppleAds?.attributionToken?.();
|
|
226
226
|
if (token) {
|
|
@@ -252,7 +252,7 @@ export async function collectInstallAttribution(client, rn) {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
async function trackDeepLinkOpen(client, url, openType, rn) {
|
|
255
|
-
const native = rn ??
|
|
255
|
+
const native = rn ?? loadReactNative();
|
|
256
256
|
const parsed = parseAttryUrl(url);
|
|
257
257
|
const openedFromUniversalLink = native?.Platform?.OS === "ios";
|
|
258
258
|
const openedFromAndroidAppLink = native?.Platform?.OS === "android";
|
|
@@ -385,14 +385,74 @@ function safeTimezone() {
|
|
|
385
385
|
return undefined;
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
|
-
|
|
388
|
+
function loadReactNative() {
|
|
389
|
+
// Avoid importing the root "react-native" namespace. In RN 0.81+, namespace
|
|
390
|
+
// imports can touch deprecated core export getters such as PushNotificationIOS.
|
|
391
|
+
const native = {
|
|
392
|
+
AppState: requireAppState(),
|
|
393
|
+
Dimensions: requireDimensions(),
|
|
394
|
+
I18nManager: requireI18nManager(),
|
|
395
|
+
Linking: requireLinking(),
|
|
396
|
+
NativeModules: requireNativeModules(),
|
|
397
|
+
Platform: requirePlatform()
|
|
398
|
+
};
|
|
399
|
+
return Object.values(native).some(Boolean) ? native : undefined;
|
|
400
|
+
}
|
|
401
|
+
function requireAppState() {
|
|
402
|
+
try {
|
|
403
|
+
return defaultExport(require("react-native/Libraries/AppState/AppState"));
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function requireDimensions() {
|
|
410
|
+
try {
|
|
411
|
+
return defaultExport(require("react-native/Libraries/Utilities/Dimensions"));
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function requireI18nManager() {
|
|
418
|
+
try {
|
|
419
|
+
return defaultExport(require("react-native/Libraries/ReactNative/I18nManager"));
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function requireLinking() {
|
|
389
426
|
try {
|
|
390
|
-
return (
|
|
427
|
+
return defaultExport(require("react-native/Libraries/Linking/Linking"));
|
|
391
428
|
}
|
|
392
429
|
catch {
|
|
393
430
|
return undefined;
|
|
394
431
|
}
|
|
395
432
|
}
|
|
433
|
+
function requireNativeModules() {
|
|
434
|
+
try {
|
|
435
|
+
return defaultExport(require("react-native/Libraries/BatchedBridge/NativeModules"));
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function requirePlatform() {
|
|
442
|
+
try {
|
|
443
|
+
return defaultExport(require("react-native/Libraries/Utilities/Platform"));
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function defaultExport(moduleValue) {
|
|
450
|
+
if (!moduleValue) {
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
const maybeDefault = moduleValue;
|
|
454
|
+
return maybeDefault.default ?? moduleValue;
|
|
455
|
+
}
|
|
396
456
|
async function createAsyncStorageAdapter() {
|
|
397
457
|
try {
|
|
398
458
|
const asyncStorageModule = (await import("@react-native-async-storage/async-storage"));
|
package/package.json
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attryio/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "React Native SDK for Attry mobile app analytics, attribution, deep links, deferred deep links, and revenue events.",
|
|
4
5
|
"private": false,
|
|
5
6
|
"license": "Apache-2.0",
|
|
6
7
|
"type": "module",
|
|
8
|
+
"homepage": "https://attry.io",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/mozhn/attry.io.git",
|
|
12
|
+
"directory": "packages/sdk-react-native"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/mozhn/attry.io/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"attry",
|
|
19
|
+
"react native analytics",
|
|
20
|
+
"react native attribution",
|
|
21
|
+
"mobile analytics",
|
|
22
|
+
"mobile attribution",
|
|
23
|
+
"deep links",
|
|
24
|
+
"deferred deep links",
|
|
25
|
+
"app analytics",
|
|
26
|
+
"campaign analytics",
|
|
27
|
+
"revenue analytics",
|
|
28
|
+
"apple search ads",
|
|
29
|
+
"install referrer"
|
|
30
|
+
],
|
|
7
31
|
"main": "./dist/index.js",
|
|
8
32
|
"types": "./dist/index.d.ts",
|
|
9
33
|
"exports": {
|
|
@@ -31,7 +55,7 @@
|
|
|
31
55
|
"access": "public"
|
|
32
56
|
},
|
|
33
57
|
"dependencies": {
|
|
34
|
-
"@attryio/sdk-core": "0.1.
|
|
58
|
+
"@attryio/sdk-core": "0.1.4",
|
|
35
59
|
"@react-native-async-storage/async-storage": "^2.2.0"
|
|
36
60
|
},
|
|
37
61
|
"peerDependencies": {
|