# Testing & Troubleshooting

Deferred flow testing has platform constraints: full attribution path depends on store redirect chain.

## Deferred flow constraints

- Deferred matching is first-launch oriented.
- Runtime callbacks do not re-trigger deferred matching logic.
- Flutter bridge does not expose session reset API in `1.1.1`.

## Recommended deferred test flow

1. Uninstall the app from the device or simulator.
2. Open a Detour link.
3. Install and launch the app.
4. Verify the initial intent and the resolved route in your UI logs.
5. Relaunch the app and confirm the deferred behavior is not repeated.

## Runtime link tests

### Android App Link

```bash
adb shell am start -a android.intent.action.VIEW \
  -d "https://<your-link-domain>/<link-token>" \
  com.example.app
```

### Android custom scheme

```bash
adb shell am start -a android.intent.action.VIEW \
  -d "myapp://product/42" \
  com.example.app
```

### iOS custom scheme (simulator)

```bash
xcrun simctl openurl booted "myapp://product/42"
```

### iOS Universal Link (simulator)

```bash
xcrun simctl openurl booted "https://<your-link-domain>/<link-token>"
```

## Platform-specific notes

### iOS

:::note[Copy the link before installing]
Clipboard matching requires that you copy the link to your clipboard before installing. Ensure `shouldUseClipboard: true` is set in `DetourConfig` and that the copy-link feature is enabled in the Detour Dashboard for your iOS configuration.
:::

### Android

Install referrer `click_id` is often unavailable in local/dev-store scenarios. In that case matching falls back to probabilistic flow.
For local install referrer testing workarounds, see
[How to test the Play Store Install Referrer API](https://medium.com/@madicdjordje/how-to-test-the-play-store-install-referrer-api-78a63d59945b).

:::note[Missing `click_id`]
If you encounter click_id missing in your production build see limitations section [Play Store referrer missing](https://detour.swmansion.com/docs/platform/architecture/architecture-limitations#play-store-referrer-missing)
:::

### Testing Android App Links

If an installed app does not open Detour links, the Dashboard SHA-256 probably does not match the key that signed **the build on your device**:

| How you install the app                     | SHA-256 to use             | Where to find it                                                                                        |
| :------------------------------------------ | :------------------------- | :------------------------------------------------------------------------------------------------------ |
| Google Play, any track                      | Play App Signing key       | Play Console → **Protected with Play** → **Manage Play app signing** → *App signing key certificate*    |
| `flutter run` / `flutter build apk --debug` | AGP default debug keystore | `keytool -list -v -keystore ~/.android/debug.keystore -storepass android`, or `./gradlew signingReport` |
| Release build with your own keystore        | Your release keystore      | `keytool -list -v -keystore <path-to-your.keystore>`                                                    |

The Flutter template signs `release` with the debug config until you add your own release keystore, so a fresh `flutter build apk --release` is still debug-signed.

To check any build directly: `apksigner verify --print-certs build/app/outputs/flutter-apk/app-debug.apk`. The complete procedure is described in [Finding your Android SHA-256 fingerprint](https://detour.swmansion.com/docs/platform/fundamentals/universal-app-links#finding-your-android-sha-256-fingerprint).

## Common issues

- **No match found (404)**
  - Check timestamps. The click may be older than the match window (15 minutes).
  - Confirm the click was recorded in your Detour Dashboard.
  - On Android deterministic flow: confirm `click_id` is present in the install referrer.

- **429 Too Many Requests**
  - Detour limits deferred link matching per device and IP address, and per
    app. Wait between reinstall tests on one device.
    For tests on many devices at once, create a separate app for testing in the
    Dashboard, because each app has its own credentials and its own limit.

- **`NOT_CONFIGURED` platform error**
  - `resolveInitialLink()` and `processLink()` raise it on both platforms. On
    Android, `logEvent()` and `logRetention()` raise it too.
  - Await `DetourService.start(...)` or `DetourFlutterPlugin.configure(...)`
    before you call these methods. If `configure(...)` failed, fix that error
    first.

- **`INVALID_ARGS` platform error**
  - Android only. `configure(...)` raises it when `apiKey` or `appID` is empty,
    `processLink(...)` when `url` is empty, and `logRetention(...)` when
    `eventName` is empty. A value that contains only whitespace counts as empty.
  - Pass non-empty values.

- **`NO_ACTIVITY` platform error**
  - On Android, `configure(...)` ran in a Flutter engine that has no attached
    activity, such as a separate engine started for background work.
  - Call `DetourService.start(...)` or `DetourFlutterPlugin.configure(...)` from
    your app's UI code, not from a background handler.

- **`NATIVE_ERROR` platform error**
  - Android only. `resolveInitialLink()` and `processLink()` raise it, and
    `linkStream` emits it as an error event, when the native SDK fails to
    process a link. `DetourService.start()` throws the same error and leaves
    `isInitialLinkProcessed` at `false`, so catch it if your startup flow waits
    for that flag.
  - A failed deferred match request on first launch raises it. When the server
    responded, the message starts with the HTTP status code: `[401]` for a
    wrong `apiKey` or `appID`, `[402]` when your organization is over its plan's
    click limit, and `[429]` for too many match requests. Without a status code,
    the request did not complete.

- **iOS clipboard not read**
  - Ensure `shouldUseClipboard: true` is set in `DetourConfig`.
  - Ensure the copy-link feature is enabled in the Detour Dashboard for your iOS app configuration.
  - The link must be copied to the clipboard before the app is opened, because the clipboard is only read during `resolveInitialLink()`.

- **Deferred link not returned on subsequent test runs**
  - Deferred matching is first-launch based. Reinstall the app for a clean re-test.

- **Android release build fails with `Type com.swmansion.detour.BuildConfig is defined multiple times`**
  - Debug builds of the same project succeed.
  - `detour_flutter_plugin` `1.1.1` uses the Android namespace
    `com.swmansion.detour`, which the native Detour SDK also uses. The release
    build then contains two classes named `com.swmansion.detour.BuildConfig`.
  - No `1.1.x` release fixes this. Upgrade to `detour_flutter_plugin: ^1.2.1`
    and run `flutter pub get`.

## Debugging

- Android:
  - `adb logcat | grep -i detour`
- iOS simulator:
  - `xcrun simctl spawn booted log stream --predicate 'subsystem == "com.swmansion.detour"'`

For full runnable setup and commands, use [Examples](https://detour.swmansion.com/docs/sdk/flutter/examples).