# Dynamic Links

Complete guide to Firebase Dynamic Links (Deep Links) in OrchardGo.

## Overview

OrchardGo uses Firebase Dynamic Links to handle deep linking on iOS and Android. Dynamic Links work as:
- **Universal Links** on iOS
- **App Links** on Android

## How Dynamic Links Work

1. User clicks a link (e.g., `https://open.theorchard.com/song/123`)
2. System checks if app is installed
3. If installed: Opens app directly
4. If not installed: Opens App Store/Play Store
5. After install: Opens app with original link

## Configuration

### Domain Setup

Dynamic link domains are configured per brand:

```
static/
└── insights.theorchard.com/
    └── .well-known/
        ├── apple-app-site-association    # iOS
        └── assetlinks.json               # Android
```

### iOS Configuration

#### 1. Apple App Site Association

Create `apple-app-site-association` file:

```json
{
  "applinks": {
    "apps": [],
    "details": [{
      "appID": "TEAM_ID.com.theorchard.OrchardGo",
      "paths": ["*"]
    }]
  }
}
```

**Important:**
- No file extension
- Must be served at `https://yourdomain.com/.well-known/apple-app-site-association`
- Must be served with `Content-Type: application/json`

#### 2. Xcode Configuration

**Associated Domains** (`orchardgo.entitlements`):
```xml
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:insights.theorchard.com</string>
  <string>applinks:open.theorchard.com</string>
</array>
```

**Info.plist**:
```xml
<!-- URL Types for custom scheme -->
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>com.theorchard.OrchardGo</string>
    </array>
  </dict>
</array>

<!-- Firebase Dynamic Links custom domains -->
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
  <string>https://open.theorchard.com</string>
  <string>https://insights.theorchard.com</string>
</array>
```

### Android Configuration

#### 1. Asset Links

Create `assetlinks.json` file:

```json
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.theorchard.OrchardGo",
    "sha256_cert_fingerprints": [
      "YOUR_CERTIFICATE_FINGERPRINT"
    ]
  }
}]
```

**Get Certificate Fingerprint:**
```bash
yarn android:certificate:fingerprint
```

Must be served at: `https://yourdomain.com/.well-known/assetlinks.json`

#### 2. AndroidManifest.xml

Add intent filters:

```xml
<activity android:name=".MainActivity">
  <!-- Other config... -->

  <!-- Dynamic Links Intent Filter -->
  <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="insights.theorchard.com" />
    <data
      android:scheme="https"
      android:host="open.theorchard.com" />
  </intent-filter>

  <!-- Custom URL Scheme -->
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data android:scheme="com.theorchard.OrchardGo" />
  </intent-filter>
</activity>
```

**Important:** `android:autoVerify="true"` enables automatic App Links verification.

## Firebase Setup

### Create Dynamic Link

1. Open [Firebase Console](https://console.firebase.google.com)
2. Select your project
3. Go to **Dynamic Links**
4. Click **New Dynamic Link**
5. Configure:
   - **Short link URL**: `https://open.theorchard.com/xyz123`
   - **Deep link URL**: `https://insights.theorchard.com/song/123`
   - **iOS behavior**: Open app or App Store
   - **Android behavior**: Open app or Play Store

### Create Programmatically

Use the script:

```bash
yarn dynamiclink:create \
  open.theorchard.com \
  https://insights.theorchard.com/song/123 \
  com.theorchard.OrchardGo \
  com.theorchard.OrchardGo \
  YOUR_APP_STORE_ID
```

This creates a long link (not shortened).

## Handling Deep Links

### React Native Implementation

```typescript
import dynamicLinks from '@react-native-firebase/dynamic-links';
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';

const App = () => {
  const navigation = useNavigation();

  useEffect(() => {
    // Handle link that opened app
    dynamicLinks()
      .getInitialLink()
      .then(link => {
        if (link) {
          handleDynamicLink(link);
        }
      });

    // Handle links while app is open
    const unsubscribe = dynamicLinks().onLink(handleDynamicLink);

    return () => unsubscribe();
  }, []);

  const handleDynamicLink = (link) => {
    const { url } = link;
    console.log('Dynamic link received:', url);

    // Parse URL and navigate
    const route = parseDynamicLink(url);
    if (route) {
      navigation.navigate(route.screen, route.params);
    }
  };

  const parseDynamicLink = (url) => {
    // Example: https://insights.theorchard.com/song/123
    const songMatch = url.match(/\/song\/(\d+)/);
    if (songMatch) {
      return {
        screen: 'Song',
        params: { id: songMatch[1] },
      };
    }

    // Example: https://insights.theorchard.com/artist/456
    const artistMatch = url.match(/\/artist\/(\d+)/);
    if (artistMatch) {
      return {
        screen: 'Artist',
        params: { id: artistMatch[1] },
      };
    }

    return null;
  };
};
```

### Link Patterns

Common link patterns:

```typescript
const LINK_PATTERNS = {
  song: /\/song\/([^/?]+)/,
  artist: /\/artist\/([^/?]+)/,
  album: /\/album\/([^/?]+)/,
  playlist: /\/playlist\/([^/?]+)/,
  profile: /\/profile\/([^/?]+)/,
};

const parseDynamicLink = (url) => {
  for (const [type, pattern] of Object.entries(LINK_PATTERNS)) {
    const match = url.match(pattern);
    if (match) {
      return {
        screen: capitalize(type),
        params: { id: match[1] },
      };
    }
  }
  return null;
};
```

## Testing Dynamic Links

### Test with Debug Parameter

Add `?d=1` to any dynamic link to see debug info:

```
https://open.theorchard.com/xyz123?d=1
```

This shows:
- Link configuration
- iOS/Android behavior
- Fallback URLs
- App Store links

### iOS Testing

1. **Build and install app**
   ```bash
   yarn start:ios --env prod
   ```

2. **Configure for testing**
   - Use production `GoogleService-Info.plist`
   - Set correct Team ID in Xcode
   - Set bundle ID: `com.theorchard.OrchardGo`
   - Verify entitlements contain domains

3. **Verify AASA file**
   - Check: `https://insights.theorchard.com/.well-known/apple-app-site-association`
   - Should return your app's configuration

4. **Test the link**
   - Send link via Messages, Mail, or Notes
   - Tap the link
   - App should open (or long-press → Open in OrchardGo)

5. **Debug with Console**
   - Xcode → Window → Devices and Simulators
   - Select device → Open Console
   - Filter for `swc` or `AASA`
   - Should see: "Beginning data task for..." or "Already downloading data for domain..."

6. **If Safari opens instead:**
   - Long press the link
   - Select "Open in OrchardGo" from menu
   - iOS will remember this choice

### Android Testing

1. **Build and install app**
   ```bash
   yarn start:android --env prod
   ```

2. **Configure for testing**
   - Use correct package name
   - Use correct signing keystore
   - Verify certificate fingerprint matches assetlinks.json

3. **Verify assetlinks.json**
   - Check: `https://insights.theorchard.com/.well-known/assetlinks.json`
   - Verify package name matches
   - Verify certificate fingerprint matches

4. **Verify with Google Tool**

   Use [Digital Asset Links Tool](https://developers.google.com/digital-asset-links/tools/generator):
   - Enter your domain
   - Enter package name
   - Verify configuration

5. **Test the link**
   ```bash
   # Send link via ADB
   yarn android:deeplink:send "https://insights.theorchard.com/song/123"
   ```

6. **Check logs**
   ```bash
   adb logcat | grep -E "IntentFilter|AssetLinks"
   ```

   Should see successful verification.

## Debugging

### iOS Debug Checklist

- [ ] `GoogleService-Info.plist` is correct for environment
- [ ] Bundle ID matches Firebase config
- [ ] Team ID is set in Xcode
- [ ] Associated Domains capability is enabled
- [ ] Entitlements file contains correct domains
- [ ] `FirebaseDynamicLinksCustomDomains` in Info.plist
- [ ] AASA file is accessible at domain
- [ ] AASA file contains correct app ID
- [ ] App is installed and device rebooted
- [ ] Check device console for AASA logs:
  - Xcode → Window → Devices and Simulators
  - Select device → Open Console
  - Search for 'swc' or 'AASA' process
  - Look for "Beginning data task for..." or "Already downloading data for domain..."

### Android Debug Checklist

- [ ] Package name matches Firebase config
- [ ] Correct keystore used for signing
- [ ] Certificate fingerprint matches assetlinks.json
- [ ] AndroidManifest.xml has intent-filter with `android:autoVerify="true"`
- [ ] All domains have assetlinks.json files
- [ ] assetlinks.json files are accessible
- [ ] App is installed
- [ ] Test with Digital Asset Links Tool
- [ ] Check logcat for verification results

### Common Issues

#### iOS: Link Opens Safari Instead of App

**Solution:**
1. Long press link → "Open in OrchardGo"
2. Check AASA file is valid JSON
3. Reboot device after installing app
4. Wait a few minutes for iOS to download AASA
5. Check device console for errors

#### Android: Link Opens Browser

**Solution:**
1. Verify `android:autoVerify="true"` in manifest
2. Check assetlinks.json is accessible
3. Verify certificate fingerprint
4. Clear app data and reinstall
5. Check logcat for verification failures

#### Link Not Parsed Correctly

**Solution:**
1. Add console logs to link handler
2. Verify regex patterns match your URLs
3. Test with different link formats
4. Handle query parameters

## Brand-Specific Configuration

Each brand needs its own dynamic link setup:

### Orchard Brand

```
Domain: insights.theorchard.com
Short URL: open.theorchard.com
Bundle ID (iOS): com.theorchard.OrchardGo
Package Name (Android): com.theorchard.OrchardGo
```

### Awal Brand

```
Domain: insights.awal.com
Short URL: open.awal.com
Bundle ID (iOS): com.awal.AwalGo
Package Name (Android): com.awal.AwalGo
```

See [Branding Guide](./branding.md) for complete brand setup.

## Creating Dynamic Links

### Backend Integration

Backend should create dynamic links for shareable content:

```graphql
mutation CreateDynamicLink($input: CreateDynamicLinkInput!) {
  createDynamicLink(input: $input) {
    shortLink
    longLink
  }
}
```

### Manual Creation

Use Firebase Console or script to create links manually for testing.

## Best Practices

1. **Test on Real Devices**
   - App Links require real devices
   - Test both fresh install and existing app

2. **Handle All Link Types**
   - App open from cold start
   - App open from background
   - Link received while app is open

3. **Graceful Fallbacks**
   - Handle unknown link formats
   - Navigate to home if parsing fails
   - Show error message if content not found

4. **Analytics**
   - Track dynamic link opens
   - Monitor conversion rates
   - Measure install attribution

5. **Security**
   - Validate link content
   - Don't trust user input in URLs
   - Handle malformed links gracefully

## Related Documentation

- [Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links)
- [iOS Universal Links](https://developer.apple.com/ios/universal-links/)
- [Android App Links](https://developer.android.com/training/app-links)
- [Branding Guide](./branding.md)
- [Configuration Guide](../getting-started/configuration.md)
