Testing · Deep links
A deep link has two jobs. The system has to hand the link to your app, and your app has to turn it into the right screen. Most bugs sit in one of those two places, so it helps to test them one at a time.
Three kinds of links
| Kind | Example | Who decides it opens your app |
|---|---|---|
| Custom scheme | myapp://orders/42 | Your app declares the scheme. No website is involved. |
| Android App Link | https://example.com/orders/42 | Android, after checking assetlinks.json on your domain. |
| iOS universal link | https://example.com/orders/42 | iOS, after reading apple-app-site-association from your domain. |
Custom schemes are not verified. Google notes that another app can register the same scheme, which can bring up a chooser (Android deep links, checked Oct 30, 2026). Apple accepts custom schemes but strongly recommends universal links. It also asks you to validate every URL parameter and to test malformed URLs (Defining a custom URL scheme, checked Oct 30, 2026).
Android: open a link with adb
Google’s test command starts a VIEW intent for one URI and one package (Android deep links, checked Oct 30, 2026):
adb shell am start -W -a android.intent.action.VIEW \
-d "myapp://orders/42" com.example.myapp
-W waits until the launch finishes, so the output shows whether it worked. In our runs, a link that no activity in the package handles failed with an error. It did not fall back to the app’s home screen. Use this command to check your intent filters and the screen each link opens.
Naming the package skips one question: which app Android would pick for the link. For a web link, ask Android the way a browser would. Google’s App Links page uses this command, with no package and the BROWSABLE category (Verify App Links, checked Oct 30, 2026):
adb shell am start -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://example.com/orders/42"
Check App Links verification
On Android 12 and later, a web link reaches your app only if your app is approved for that domain. Otherwise it opens in the default browser (Android deep links, checked Oct 30, 2026). Verification approves your app without anyone touching settings. A person can also approve a domain by hand, in the app’s settings (Verify App Links, checked Oct 30, 2026). Verification needs two things:
- At least one intent filter needs
android:autoVerify="true". - Each host in those filters serves
https://<host>/.well-known/assetlinks.json.
To re-run verification on a device or emulator with Android 12 or later, Google lists these commands (Verify App Links, checked Oct 30, 2026):
-
Reset the link state. This puts the device back to how it was before anyone chose a default app for your domains.
adb shell pm set-app-links --package com.example.myapp 0 all -
Verify again.
adb shell pm verify-app-links --re-verify com.example.myapp -
Read the result. A domain that passed shows
verified.adb shell pm get-app-links com.example.myapp
A state of none means nothing is recorded yet: wait a few minutes and verify again. A number of 1024 or higher is an error code from the device’s verifier. Google’s advice is to check the network connection and run verification again.
iOS: open a link with simctl
xcrun simctl openurl booted "myapp://orders/42"
booted targets the running simulator. With several running, simctl picks one of them, so pass a UDID instead (xcrun simctl help). If no app on the simulator claims the scheme, the command fails with an error that it could not open the URL.
For a custom scheme, the app declares it under URL Types on the target’s Info tab in Xcode. Expo apps set a scheme in the app config. After you add one, Expo says you need a new development build (Linking into your app, checked Oct 30, 2026).
Check a universal link
A universal link needs two parts: an entitlement in the app and a file on your site. The app needs the Associated Domains capability with an entry such as applinks:example.com. Your site serves https://example.com/.well-known/apple-app-site-association over HTTPS, with a valid certificate and no redirects (Supporting associated domains, checked Oct 30, 2026).
Apple’s debugging technote adds the details that trip people up (TN3155, checked Oct 30, 2026):
- Match each entry to a file. A file at the root domain serves
applinks:example.comand the wildcardapplinks:*.example.com. A named subdomain such asapplinks:www.example.comneeds its own file on that subdomain. - No ports.
applinks:example.com:8080will not open your app. - Serve the file to everyone. A 301 or 302 means a redirect, which is not supported for this file. A 403 or 404 usually means the file is not reachable from every address. Your server should also accept any user agent.
- Check the file from a Mac.
curl -v https://example.com/.well-known/apple-app-site-associationshows the headers. Save a copy withcurl -o aasa.json https://example.com/.well-known/apple-app-site-association, thensudo swcutil verify -d example.com -j ./aasa.json -u https://example.com/orders/42checks a path against it. - Typing the URL in Safari never opens the app. That counts as browsing. A link to the same domain you are already browsing stays in the browser too.
Why a fixed file can still fail
On iOS 14 and later, Apple’s CDN fetches and caches your association file. Devices download it from the CDN when the app is installed, then check about once a week. To get a newer file, reinstall the app; the CDN has no purge (TN3155, checked Oct 30, 2026). So a file you just fixed can work on one install and not another.
While you develop against a server the CDN cannot reach, add ?mode=developer to the entitlement entry. Apple limits that mode to apps signed with a development profile, on devices where the user opted in (Associated Domains Entitlement, checked Oct 30, 2026).
Apple’s on-device check is the Associated Domains Development diagnostics, under Settings > Developer once Developer Mode is on. Its deeper check, the swcutil_show.txt report, comes from a sysdiagnose taken on a device. Each device also stores a person’s choice between the app and the browser. Use a simulator to test your routing quickly, and confirm a universal link on a physical iPhone before release.
Common failures
| What you see | Likely cause |
|---|---|
| adb: “unable to resolve Intent” | No activity in the package has an intent filter for that scheme and host. |
| An https link opens Chrome, not the app | The domain is not verified. Run pm get-app-links and check assetlinks.json. |
| simctl: “failed to open” the URL | No installed app declares the scheme, or the build predates the change. |
| A universal link opens Safari | A wrong entitlement or file path, a redirect, an old cached file, or a stored choice for the browser. |
| The app opens on the wrong screen | The link reached the app, so the bug is in your router: the path, the parameters, or a screen behind sign-in. |
| The app shows a “not found” screen | The router has no route for that path. Check how your router reads the host and path of the URL. |
The last row is easy to miss. In our run on VibeView’s Atlas sample app, atlas://saved opened the Saved tab. atlas://home reached the app but matched no route. Expo Router showed its Unmatched Route screen, which lists the URL as it read it. Both links “opened the app”. Only one opened the right screen.

Also test links while the app is already running, not only from a cold start. Many apps read the first URL at launch and handle later URLs in a different place.
Open deep links on VibeView
VibeView runs your app on cloud iOS simulators and Android emulators, and on Apple TV, Android TV and Roku. On iPhone, iPad and Android phones and tablets, you can open a deep link in four ways: the command line, an MCP client, the Devices page, or an AI test step (beta). On Apple TV and Android TV, the same works except the Devices page. Roku works differently, as described below.
From the CLI, start a session with your app and open the link:
vibeview dev --detach --platform ios --app <app-id> --standalone
vibeview open-url "atlas://saved"
vibeview screenshot --out saved.png
vibeview dev-stop
open-url prints ok and a summary of what changed on screen, so you can confirm the link worked without a screenshot. The same command is available as an MCP tool for coding agents. See AI Agent Control.


What each platform accepts:
- Android and Android TV. The link goes only to the app under test. A link the app has no screen for fails with an error that says so, and nothing opens in a browser. That covers custom schemes and the https links your intent filters declare.
- iPhone, iPad and Apple TV. Use your app’s own URL scheme. The CLI and MCP refuse web links and system links such as
tel:, and so does the Device tab on iPhone and iPad, so a link does not end up in Safari. Test universal links with the local steps above. The first time a link goes to your app, the system asks to open it in the app. VibeView answers Open when the prompt names your app. - Roku. There is no
open-url. A Roku deep link is a launch withcontentIdandmediaType, which the channel reads inMain(args)(Roku deep linking, checked Oct 30, 2026). VibeView’s launch params arrive in the sameargs, so you can start a channel with those two keys through the REST API or an embed URL.
On a phone or tablet, the Device tab of the tools panel has an Open link field for custom-scheme links. It is not available while you record a test. See The Device tab.
In a test, a step such as “Open atlas://saved and check that the Saved tab shows” lets the AI agent open the link and check the screen. On iPhone and iPad, keep these steps to your app’s own scheme: an https link in an AI step can open Safari instead of your app. AI testing is in beta. See AI testing.
Frequently asked questions
Does the adb command with a package name test App Links verification?
No. Naming the package sends the link straight to your app, so it tests the intent filter and the screen. To see what Android does with a web link, run the command without the package and with the BROWSABLE category, then check the state with pm get-app-links.
Can I test a universal link on the iOS simulator?
You can open one with simctl openurl and check your routing. The sysdiagnose report comes from a device, the association file is cached per install, and a person’s choice is stored per device. Confirm on a physical iPhone before release.
Why does my universal link open Safari after I fixed the file?
Devices get the file through Apple’s CDN when the app is installed and check about once a week. Reinstall the app to fetch the new file, or add mode=developer to your entitlement entry while you work.
Can VibeView open https links on iOS?
Not from the CLI or MCP. On iPhone, iPad and Apple TV they refuse web links, as does the Device tab on iPhone and iPad. An AI test step can send an https link, but on iOS it may open Safari instead of your app. Test universal links with the local steps above. On Android and Android TV, https links your app declares are delivered to your app.
Do deep links work on TV?
Yes. On Android TV the same intents and adb commands apply. On Apple TV, custom schemes work, and tvOS asks the first time a link opens the app. Roku uses launch parameters instead, with contentId and mediaType.
Open your own links on a live device
Upload your build, start a session, and open your own links. Plans include a free tier, so you can check a few links before you pay for anything.
Related guides
In the documentation
Try it yourself
Open a live iOS simulator, Android emulator, Apple TV, Android TV or Roku in your browser.
Start free