@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.
Files changed (3) hide show
  1. package/README.md +117 -9
  2. package/dist/index.js +69 -9
  3. 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
- The SDK auto-tracks first SDK install, app opens, sessions, foreground/background transitions, deep link opens, Apple AdServices tokens on iOS, and Google Play Install Referrer on Android when the native modules are available.
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
- - Lifecycle: `install`, `open`, `session_started`, `session_ended`, `app_foreground`, `app_background`, `deep_link_opened`
35
- - Revenue: `purchase`
36
- - Commerce/content: `initiate_purchase`
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
- `identify()` is a convenience method that attaches a known user ID to later events; it is not sent as a standard event. Any other product action can still be sent with `track("your_event_name", { properties })`.
146
+ ## Need help?
39
147
 
40
- Purchase events must include `value` or `amountMinor` plus `currency`. Attry stores them as stable minor-unit revenue fields so dashboard revenue is not guessed from arbitrary properties.
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.6";
3
+ const SDK_VERSION = "0.1.8";
4
4
  export { parseAttryUrl };
5
5
  export async function createAttryReactNative(config) {
6
- const rn = await loadReactNative();
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 ?? (await loadReactNative());
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 ?? (await loadReactNative());
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 ?? (await loadReactNative());
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 ?? (await loadReactNative());
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 ?? (await loadReactNative());
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
- async function loadReactNative() {
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 (await import("react-native"));
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.6",
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.3",
58
+ "@attryio/sdk-core": "0.1.4",
35
59
  "@react-native-async-storage/async-storage": "^2.2.0"
36
60
  },
37
61
  "peerDependencies": {