npm package: @purple-links/react-native
Description: Purple Links – lightweight React Native deep linking SDK (session-only)
Author: Olayiwola Oni
Latest version: 0.1.16

# 🟣 Purple Links SDK

Install-aware deep linking & referral attribution for Android & iOS.

No fingerprinting.
No guesswork.
OS-backed attribution.

---

# 📦 Installation

```bash
npm install @purple-links/react-native
```

or

```bash
yarn add @purple-links/react-native
```

---

# 🧱 1️⃣ Initial Setup (Required Once Per Organization)

You must host two domain files:

## Android — `assetlinks.json`

**How to get this file:**

* **Option 1 (Easiest):** Go to your **Google Play Console**. Navigate to **Release** -> **Setup** -> **App Integrity** -> **App Signing**. You can copy the exact `Digital Asset Links JSON` snippet provided there.
* **Option 2:** Use **Android Studio**. Go to **Tools** -> **App Links Assistant** -> **Open Digital Asset Links File Generator**.
* **Option 3:** Create it manually. You will need your app's `package_name` and the `sha256_cert_fingerprints` of your signing keystore (you can get this by running `keytool -list -v -keystore my-release-key.keystore`).

**Where to host it:**

```text
https://go.yourdomain.com/.well-known/assetlinks.json
```

**Example `assetlinks.json`:**

```json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.your.app",
      "sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
    }
  }
]
```

---

## iOS — `apple-app-site-association` (AASA)

**How to get this file:**
You create this file manually. You need two pieces of information:

1. **Team ID:** Found in your Apple Developer Account under Membership details.
2. **Bundle Identifier:** Your app's bundle ID (e.g., `com.your.app`).

Combine them as `TEAMID.BundleIdentifier` (e.g., `ABCDE12345.com.your.app`) and place it in the `appID` field.

**Where to host it:**

```text
https://go.yourdomain.com/.well-known/apple-app-site-association
```

*(Also valid at the root: `https://go.yourdomain.com/apple-app-site-association`)*

**Example `apple-app-site-association`:**

```json
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "ABCDE12345.com.your.app",
        "paths": ["*"]
      }
    ]
  }
}
```

**Hosting Requirements:**

* Must be served over **HTTPS** with a valid certificate.
* **No redirects**.
* Must have `Content-Type: application/json` headers (even though the file has no extension).
* **CRITICAL:** The file name must be exactly `apple-app-site-association` (no `.json` extension).

---

# 📱 2️⃣ App Setup

---

## 🔵 Android Setup

### Add intent filter

In `AndroidManifest.xml`:

```xml
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />

  <data
    android:scheme="https"
    android:host="go.yourdomain.com" />
</intent-filter>
```

---

### Install Referrer dependency (auto-linked if RN)

```gradle
implementation 'com.android.installreferrer:installreferrer:2.2'
```

---

## 🍎 iOS Setup

### Enable Associated Domains

In Xcode:

Signing & Capabilities → Associated Domains

Add:

```
applinks:go.yourdomain.com
```

Rebuild app.

---

# 🚀 3️⃣ Initialize SDK

Do this once at app startup:

```ts
import PurpleLinks from '@purple-links/react-native';

PurpleLinks.init({
  api_key: 'your_api_key',
}, true); // showLogs is true by default
```

### `init` Options

| Field | Type | Description |
| --- | --- | --- |
| `api_key` | `string` (Required) | Your PurpleLinks API Key. |
| `showLogs` | `boolean` (Optional) | Set to `false` to disable SDK console logging. Defaults to `true`. |
| `environment` | `string` (Optional) | `production` or `staging`. |

---

# 🔗 4️⃣ Generate Referral Link

```ts
const { url } = await PurpleLinks.generateLink({
  campaign: 'referral',
  identifier: user.id,
  navigate: 'referral-screen',
  metadata: {
    reward: 500
  }
});
```

Share `url`.

## `generateLink` Options (`LinkData`)

| Field | Type | Description |
| --- | --- | --- |
| `campaign` | `string` (optional) | The marketing campaign name. |
| `source` | `string` (optional) | The source of the traffic (e.g., facebook, twitter, email). |
| `medium` | `string` (optional) | The marketing medium (e.g., cpc, banner). |
| `content` | `string` (optional) | Differentiates similar content, or links within the same ad. |
| `term` | `string` (optional) | Identifies paid keywords. |
| `identifier` | `string` (optional) | A custom user identifier for the referral or inviter. |
| `navigate` | `string` (optional) | The screen, route, or path to navigate to when the link is opened. |
| `metadata` | `object` (optional) | Any custom key-value pairs (e.g., `{ reward: 500, tier: 'gold' }`). |

---

# 🎯 5️⃣ Resolve Referral On App Launch

```ts
useEffect(() => {
  async function resolve() {
    const data = await PurpleLinks.deepLink();

    if (!data) return;

    if (data.navigate) {
      router.navigate(data.navigate, data.metadata);
    }
  }

  resolve();
}, []);
```

## 🔔 Attribution Event (Recommended)

Instead of manually polling, you can listen for attribution events as soon as they are detected (e.g., from an Install Referrer or Deep Link).

```ts
import { NativeEventEmitter, NativeModules } from 'react-native';
const { PurpleLinksModule } = NativeModules;
const eventEmitter = new NativeEventEmitter(PurpleLinksModule);

useEffect(() => {
  const subscription = eventEmitter.addListener('PurpleLinksAttributionReceived', (data) => {
    console.log('Attribution received:', data.code, data.source);
    // { code: "ABC123", source: "install_referrer" }
  });

  return () => subscription.remove();
}, []);
```

## 📂 Manual Retrieval

You can retrieve the attribution data at any time after it has been captured:

```ts
const attribution = await PurpleLinks.getAttribution();
if (attribution) {
  console.log('Attribution Code:', attribution.code);
  console.log('Source:', attribution.source);
}
```

## `deepLink` Return Data (`ResolvedLink`)

Returns `Promise<ResolvedLink | null>`. It resolves to `null` if no referral data is found. Otherwise, it returns an object containing:

| Field | Type | Description |
| --- | --- | --- |
| `navigate` | `string` (optional) | The screen, route, or path the user should be routed to. |
| `metadata` | `any` (optional) | The custom metadata payload attached to the generated link. |
| `[key: string]` | `any` | Any additional properties or parameters parsed from the deep link or returned by the backend. |

---

# 🔁 How It Works Internally

## Android

Installed:

* App Link opens app
* Intent parsed
* Referral stored

Not Installed:

* Redirect to Play Store with `referrer`
* SDK reads Install Referrer API on first launch
* Referral stored permanently

---

## iOS

Installed:

* Universal Link opens app
* Referral parsed

Not Installed:

* Click stored server-side
* On first launch SDK calls `/resolve-install`
* Server matches and returns referral

---

# 🧠 Referral Storage Rules

* Stored once per install
* Cannot be overwritten
* `clear()` required to reset

---

# 🧪 Testing

Android:

```bash
adb shell am start -a android.intent.action.VIEW \
-d "https://go.yourdomain.com/r/ABC123"
```

iOS:

```bash
xcrun simctl openurl booted \
"https://go.yourdomain.com/r/ABC123"
```

---

# 🔐 Security Best Practices

* Sign link payloads server-side
* Validate signature in SDK
* Use short expiry for referral tokens
* Prevent duplicate reward claims

---

# 🎯 Final Result

| Scenario          | Works                   |
| ----------------- | ----------------------- |
| Installed Android | ✅                       |
| Android Install   | ✅                       |
| Installed iOS     | ✅                       |
| iOS Install       | ✅ (probabilistic match) |