Skip to main content

Troubleshoot & Test Deep Links

A comprehensive guide to diagnosing and fixing deep link issues in your app. Use this page whenever links fail to route into your app or when you need to verify your deep link configuration.

Quick Diagnosis

Before diving into detailed troubleshooting, run through this quick checklist:

  • SDK Initialized: Check logs for Bootstrap completed successfully or similar initialization messages
  • API Key Valid: Ensure your project API key matches the environment (production vs development)
  • Domain Configuration: URL scheme/intent filter matches the domain configured in ULink dashboard
  • Domain Verified: Domain shows as "Verified" in Domains section of dashboard
  • Platform Configuration: iOS (Bundle ID, Team ID) or Android (Package Name, SHA-256) configured in dashboard
  • Network Access: Device has internet access (no captive portal blocking requests)
  • Link Created: The link exists in your ULink dashboard and has been clicked at least once
Automatic File Serving

ULink automatically serves the required verification files (AASA for iOS, Asset Links for Android) on your domain. You don't need to:

  • Host these files yourself
  • Deploy them to your server
  • Configure your web server

The files are generated automatically based on your dashboard configuration and served at:

  • iOS: https://<your-domain>/.well-known/apple-app-site-association
  • Android: https://<your-domain>/.well-known/assetlinks.json

If these files aren't accessible, check that your domain is verified and platform configuration is complete in the dashboard.

If all items are checked but links still don't work, proceed to the platform-specific sections below.

1. Basic Testing Methods

Test your deep links using these methods before diving into detailed troubleshooting.

iOS Testing

  1. Open the Notes app or Messages on your iOS device
  2. Paste your ULink URL (e.g., https://links.shared.ly/your-slug)
  3. Tap the link
  4. The app should open if universal links are configured correctly
Why This Works

Sharing via Notes or Messages uses the same system that handles universal links in Safari, making it the most realistic test scenario.

Method 2: Simulator Command

xcrun simctl openurl booted "https://links.shared.ly/your-slug"

This command simulates tapping a link in the iOS Simulator. Check the console for link processing logs.

Method 3: Safari Browser

  1. Open Safari on your device
  2. Navigate to your ULink URL
  3. Tap the "Open in App" banner if it appears
  4. Or long-press the link and select "Open in [Your App]"
Universal Links Behavior

Universal links only work when:

  • The link is opened from another app (not Safari directly)
  • The domain is properly associated with your app
  • The AASA file is accessible and valid

Android Testing

adb shell am start \
-a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://links.shared.ly/your-slug"

This command simulates tapping a link. Your app should open and process the link.

Method 2: Share Sheet

  1. Share the link from any app (Chrome, Messages, etc.)
  2. Select your app from the share options
  3. The app should open and handle the link

Method 3: Chrome Browser

  1. Open Chrome on your device
  2. Navigate to your ULink URL
  3. If App Links are verified, the app opens automatically
  4. If not verified, you'll see an "Open in App" banner

Flutter Testing

For Flutter apps, use the platform-specific methods above. Additionally, check for this log message:

DEBUG: Link listeners setup complete

This confirms the Flutter SDK has initialized and is ready to receive links. If you don't see this message, the SDK may not be initialized properly.

2. Inspect Logs and Debug Output

Logs are your best friend when troubleshooting deep links. Here's what to look for:

iOS Logs

Xcode Console

When testing in Xcode, look for these log messages:

[ULink] Handling incoming URL: https://links.shared.ly/your-slug
[ULink] Processing URL: https://links.shared.ly/your-slug
[ULink] Successfully resolved ULink data: {...}

Console.app (Physical Devices)

  1. Connect your device to your Mac
  2. Open Console.app
  3. Select your device from the sidebar
  4. Filter by your bundle ID or search for "ULink"
  5. Tap your link and watch for processing messages

Common iOS Log Patterns

Success Pattern:

[ULink] Handling incoming URL: ...
[ULink] Processing URL: ...
[ULink] Successfully resolved ULink data: ...

Failure Pattern:

[ULink] Handling incoming URL: ...
[ULink] URL is not a ULink or resolution failed

Android Logs

Logcat

Use Android Studio's Logcat or command line:

adb logcat | grep -i ulink

Look for these messages:

ULink: Handling deep link: https://links.shared.ly/your-slug
ULink: Resolved link data:
ULink: Type: dynamic
ULink: Slug: your-slug
ULink: Parameters: {...}

Common Android Log Patterns

Success Pattern:

ULink: Handling deep link: ...
ULink: Resolved link data:
ULink: Type: dynamic

Failure Pattern:

ULink: Handling deep link: ...
ULink: Error resolving link: ...

Flutter Logs

Flutter apps show logs from both the Flutter layer and native platforms:

DEBUG: Setting up link listeners
DEBUG: Link listeners setup complete
DEBUG: Dynamic link event received: {...}
DEBUG: UI updated with dynamic link event

If you see DEBUG: Link listeners setup complete but no link events, the native platform may not be forwarding links correctly.

3. Common Issues and Solutions

iOS:

  • Cause: Universal links not properly associated or AASA file invalid
  • Solution: See "Verify iOS Universal Links" section below

Android:

  • Cause: App Links not verified or intent filter misconfigured
  • Solution: See "Verify Android App Links" section below

Issue: App Opens But Doesn't Navigate

Possible Causes:

  1. Stream listeners not set up
  2. Listening to wrong stream (dynamic vs unified)
  3. Parameters not being accessed correctly
  4. Navigation logic has errors

Solutions:

  1. Verify stream listeners are set up after SDK initialization
  2. Check you're listening to the correct stream:
    • dynamicLinkStream / onDynamicLink for dynamic links
    • unifiedLinkStream / onUnifiedLink for unified links
  3. Add debug logging to see what data is received
  4. Check navigation code for errors

Possible Causes:

  1. Domain association not trusted on device
  2. App not reinstalled after configuration changes
  3. Cached association data

Solutions:

  1. Delete and reinstall the app (forces re-verification)
  2. Clear app data and cache
  3. For iOS: Reset network settings
  4. For Android: Force re-verification with ADB

Possible Causes:

  1. Not checking for initial links on app launch
  2. persistLastLinkData disabled
  3. Initial link check happens too late

Solutions:

  1. Always check getInitialDeepLink() when app starts
  2. Enable persistLastLinkData in your config
  3. Check for initial links before setting up stream listeners

Possible Causes:

  1. Wrong stream subscription
  2. Links opening in browser instead of app
  3. Domain/configuration mismatch

Solutions:

  1. Verify you're subscribed to both dynamic and unified streams
  2. Check that links are actually opening the app (not browser)
  3. Verify domain in dashboard matches manifest/Info.plist configuration
  4. Check logs to see if links are being processed

Universal links require proper domain association. ULink automatically serves the Apple App Site Association (AASA) file for you - you don't need to host it yourself. Follow these steps to verify your configuration.

Step 1: Check the AASA File

ULink serves the AASA file automatically at your domain. Verify it's accessible:

curl https://<your-domain>/.well-known/apple-app-site-association

Expected Output:

{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourapp",
"paths": [
"NOT /__/auth/action/",
"NOT /__/auth/handler/",
"NOT /_/*",
"/*"
]
}
]
},
"webcredentials": {
"apps": ["TEAM_ID.com.yourapp"]
}
}

Common Issues:

  • 404 Not Found: Domain not verified in ULink dashboard or not configured correctly
  • HTML Response: Domain may not be pointing to ULink's servers correctly
  • Missing Bundle ID: iOS configuration (Bundle ID and Team ID) not set in ULink dashboard
  • Empty details array: iOS app configuration missing in Configuration → General → iOS
AASA File is Served Automatically

ULink automatically generates and serves the AASA file for your domain. You don't need to:

  • Host the file yourself
  • Deploy it to your server
  • Configure your web server

The file is generated based on your iOS configuration in the ULink dashboard:

  • Bundle ID: Set in Configuration → General → iOS → Bundle Identifier
  • Team ID: Set in Configuration → General → iOS → Team ID

If the file is not accessible, check:

  1. Domain is verified in Domains section of dashboard
  2. Domain DNS is pointing to ULink's servers
  3. iOS configuration is complete in dashboard
xcrun simctl openurl booted "https://<your-domain>/<slug>"

Expected Behavior:

  • App opens immediately (no disambiguation dialog)
  • Link is processed and streams emit data

If App Doesn't Open:

  • Check Associated Domains in Xcode (Signing & Capabilities)
  • Verify domain format: applinks:<your-domain> (no https://)
  • Ensure domain matches exactly what's in dashboard

Step 3: Monitor Association Logs (Physical Device)

On a physical device, monitor association logs:

log stream --predicate 'subsystem == "com.apple.applinks"' --style compact

Then tap your link and watch for:

  • accepted - Link association is trusted
  • denied - Link association failed

If You See "denied":

  1. Verify Associated Domains capability is added in Xcode
  2. Check domain format in Xcode matches dashboard exactly
  3. Verify domain is verified in ULink dashboard
  4. Ensure iOS configuration (Bundle ID, Team ID) is set in dashboard
  5. Delete and reinstall app (forces re-verification)
  6. Verify AASA file is accessible via curl (see Step 1)

Step 4: Verify Xcode Configuration

  1. Open your project in Xcode
  2. Select your target → Signing & Capabilities
  3. Verify Associated Domains capability is added
  4. Check domain entries:
    • Format: applinks:yourdomain.com (no https://)
    • Must match domain in ULink dashboard exactly
    • No trailing slashes or paths

Android App Links require Digital Asset Links verification. ULink automatically serves the Asset Links file for you - you don't need to host it yourself. Follow these steps.

ULink serves the Asset Links file automatically at your domain. Verify it's accessible:

curl https://<your-domain>/.well-known/assetlinks.json

Expected Output:

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourapp",
"sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
}
}]

Common Issues:

  • 404 Not Found: Domain not verified in ULink dashboard or not configured correctly
  • HTML Response: Domain may not be pointing to ULink's servers correctly
  • Wrong Fingerprint: SHA-256 fingerprint doesn't match what's configured in dashboard
  • Missing Package: Android configuration (Package Name) not set in ULink dashboard
  • Empty array: Android app configuration missing in Configuration → General → Android
Asset Links File is Served Automatically

ULink automatically generates and serves the Asset Links file for your domain. You don't need to:

  • Host the file yourself
  • Deploy it to your server
  • Configure your web server

The file is generated based on your Android configuration in the ULink dashboard:

  • Package Name: Set in Configuration → General → Android → Package Name
  • SHA-256 Fingerprints: Set in Configuration → General → Android → SHA-256 Certificate Fingerprints

If the file is not accessible, check:

  1. Domain is verified in Domains section of dashboard
  2. Domain DNS is pointing to ULink's servers
  3. Android configuration is complete in dashboard
  4. SHA-256 fingerprints match your app signing key exactly

Verify your domain association using Google's Asset Links Tool:

  1. Enter your domain
  2. Enter your package name
  3. Click "Test Statement"
  4. Verify it shows as "Verified"

Use ADB commands to check verification status (replace com.example.app with your package name):

# Show current status for each host
adb shell pm get-app-links com.example.app

Expected Output:

com.example.app:
ID: 1234567890abcdef
Signatures: [abc123...]
Domain verification state:
links.shared.ly: verified
yourdomain.com: verified

Status Values:

  • verified - Domain is properly associated ✅
  • approved - User manually approved (shows disambiguation dialog)
  • none - Not verified, will show disambiguation dialog
  • legacy_failure - Verification failed
  • no_response - No response from server

Step 4: Force Re-verification

If status shows none or legacy_failure, force re-verification:

# Force Android to re-check Digital Asset Links
adb shell pm verify-app-links --re-verify com.example.app

# Wait a few seconds, then check status again
adb shell pm get-app-links com.example.app

After verification succeeds, test the link:

adb shell am start \
-a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://<your-domain>/<slug>"

Expected Behavior:

  • App opens immediately (no disambiguation dialog)
  • Link is processed and streams emit data

Status Shows "none" or "legacy_failure"

  1. Check Digital Asset Links JSON:

    curl https://<your-domain>/.well-known/assetlinks.json

    Verify it contains your package name and correct SHA-256 fingerprint. If it returns 404 or empty array, the domain may not be verified or Android configuration is missing in dashboard.

  2. Verify Domain Configuration in Dashboard:

    • Confirm domain is listed and verified in Domains section
    • Check domain DNS is pointing to ULink's servers
    • Verify domain status shows as "Verified"
  3. Verify Android Configuration in Dashboard:

    • Go to Configuration → General → Android
    • Ensure Package Name matches your app's package name exactly
    • Verify SHA-256 Certificate Fingerprints are set and correct
    • Check that fingerprints match your app signing key
  4. Check SHA-256 Fingerprint:

    # Debug keystore
    keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

    # Release keystore
    keytool -list -v -keystore your-release-key.keystore -alias your-alias

    Ensure the SHA-256 matches what's configured in the ULink dashboard (not just what's in the JSON, since ULink generates it from dashboard config)

  5. Re-run Verification:

    adb shell pm verify-app-links --re-verify com.example.app

App Opens But Shows Disambiguation Dialog

This means App Links are not verified. The user must manually select your app. To fix:

  • Follow the verification steps above
  • Ensure android:autoVerify="true" is set in your intent filter
  • Verify Digital Asset Links JSON is correct

6. Platform-Specific Debugging

iOS Debugging Checklist

  • AASA file accessible and returns valid JSON
  • Associated Domains capability added in Xcode
  • Domain format correct: applinks:yourdomain.com
  • Domain matches ULink dashboard configuration
  • App reinstalled after configuration changes
  • URL handling methods implemented in AppDelegate
  • Stream listeners set up and active
  • Debug logs show link processing

Android Debugging Checklist

  • Asset Links JSON accessible and returns valid JSON
  • SHA-256 fingerprint matches signing key
  • Intent filter has android:autoVerify="true"
  • Domain in intent filter matches dashboard
  • App Links status shows verified in ADB
  • Stream listeners set up in coroutine scope
  • Debug logs show link processing
  • App reinstalled after configuration changes

Flutter Debugging Checklist

  • Native platforms configured (iOS and Android separately)
  • SDK initialized before setting up listeners
  • DEBUG: Link listeners setup complete appears in logs
  • Stream subscriptions active (not cancelled)
  • Both onDynamicLink and onUnifiedLink listeners set up
  • Initial link checked on app start
  • Platform-specific issues resolved (see iOS/Android checklists)

7. Advanced Troubleshooting

Enable Maximum Debug Logging

iOS:

let config = ULinkConfig(
apiKey: "your-api-key",
debug: true // Enable debug logging
)

Android:

val config = ULinkConfig(
apiKey = "your-api-key",
debug = true // Enable debug logging
)

Flutter:

await ULink.instance.initialize(
ULinkConfig(
apiKey: 'your-api-key',
debug: true, // Enable debug logging
),
);

You can test if a link resolves correctly without opening the app:

iOS:

Task {
let url = URL(string: "https://links.shared.ly/your-slug")!
let resolved = await ULink.shared.processULinkUrl(url)
print("Resolved: \(resolved?.slug ?? "nil")")
}

Android:

lifecycleScope.launch {
val uri = Uri.parse("https://links.shared.ly/your-slug")
val resolved = ulink.resolveLink(uri.toString())
Log.d("ULink", "Resolved: ${resolved.data}")
}

Flutter:

final response = await ULink.instance.resolveLink('https://links.shared.ly/your-slug');
if (response.success) {
print('Resolved: ${response.data}');
}

Verify Stream Subscriptions

Ensure your stream listeners are actually receiving data:

iOS:

ULink.shared.dynamicLinkStream
.sink { data in
print("✅ Received dynamic link: \(data.slug ?? "N/A")")
}
.store(in: &cancellables)

Android:

lifecycleScope.launch {
ulink.dynamicLinkStream.collect { data ->
Log.d("ULink", "✅ Received dynamic link: ${data.slug}")
}
}

Flutter:

_sdk.onDynamicLink.listen((data) {
debugPrint('✅ Received dynamic link: ${data.slug}');
});

8. Still Stuck?

If you've gone through all the troubleshooting steps and links still don't work, gather this information:

Information to Collect

  1. Link Details:

    • The exact link URL you're testing
    • Link slug from ULink dashboard
    • Link type (dynamic or unified)
  2. Platform Information:

    • Platform (iOS/Android/Flutter)
    • OS version
    • Device model
    • App version
  3. Configuration:

    • Domain configured in dashboard
    • Bundle ID / Package name
    • URL scheme (if using custom schemes)
  4. Logs:

    • Relevant console/logcat output
    • SDK initialization logs
    • Link processing logs (if any)
    • Error messages
  5. Test Results:

    • What happens when you tap the link?
    • Does the app open?
    • Do streams emit data?
    • Any error messages?

Get Help

Email support@ulink.ly with:

  • Subject: "Deep Link Troubleshooting - [Your Issue]"
  • All the information collected above
  • Screenshots of relevant logs or configurations

We'll review your configuration and help you get links working!