How to Test Universal Links on iOS
Test Universal Links in the iOS Simulator and on a real iPhone: xcrun simctl openurl, AASA validation, and fixes when Universal Links aren't working.
Short answer: you can test Universal Links in the iOS Simulator. Install your app on a booted simulator, then run xcrun simctl openurl booted "https://example.com/path". The Simulator can send https:// links to Safari, or show an "Open in app?" prompt, even when the association works, so treat it as a routing check and confirm on a real iPhone. If the link opens Safari on a real iPhone, it isn't working. Check three things in order: your app has the applinks: Associated Domains entitlement for that exact host, your apple-app-site-association (AASA) file is served over https:// at /.well-known/ with no redirects, and the path matches your components rules. Then repeat the test on a real iPhone by tapping the link in Messages, Mail or Notes.
How do you test Universal Links in the iOS Simulator?
- Build and run your app on a simulated iPhone so the app is installed and the simulator is booted.
- In Terminal, open the link on the booted simulator:
xcrun simctl openurl booted "https://example.com/buy/42"
simctl is Apple's command-line tool for simulated devices. Run xcrun simctl help to list its subcommands (Apple: Interacting with devices using the command line). Usage is xcrun simctl openurl <device> <URL>, where <device> is a simulator UDID or booted. On the Simulator, results for https:// links are inconsistent. Some developers see the app open; others see Safari, sometimes with an "Open in app?" prompt, even when Associated Domains and the AASA file are correct. Apple doesn't document this behaviour. So check routing with your custom URL scheme, for example xcrun simctl openurl booted "myapp://buy/42", and confirm true Universal Link behaviour on a real iPhone.
3. Check the result:
- The app opens to the right screen. The association works and your routing works.
- Safari opens, or an "Open in app?" prompt appears. On the Simulator this can happen even when the association works. Test the same link on a real iPhone; if Safari opens there, the link isn't working. See the next section.
- The app opens to the wrong screen. The association works, but your routing code is wrong.
4. Run the same URL list with the app not running and with it in the background. Those are two different code paths (see "How do I set up the app side?" below).
5. You can also paste the link into Notes on the simulator and tap it. Apple's own test method is to long-press the link in Notes and check that both "open in app" and "open in browser" options appear (Apple TN3155: Debugging universal links).
Testing against a staging server. If your AASA file is on a server Apple's CDN can't reach, add an alternate mode to the entitlement: applinks:staging.example.com?mode=developer. Apple suggests this mode when you run the app from Xcode or change the file faster than the CDN caches it (Apple: Associated Domains entitlement; TN3155). Remove it before you release.
Why are Universal Links not working?
Apple's debugging technote (TN3155) and the associated domains docs cover the common causes:
| Symptom | Likely cause | Fix |
|---|---|---|
| Safari opens every time | No AASA file on that exact host, or a mismatch between applinks: and the host |
Each subdomain in applinks: needs its own AASA file. applinks:example.com doesn't cover www.example.com. |
| Safari opens every time | AASA response is a 301 or 302 redirect, or a 403 or 404 | Serve the file directly on every domain, to every IP address and user agent |
| Works on one device, not another | Someone chose "open in browser" for that domain | Long-press the link in Notes and choose the app option. That choice becomes the device's default. |
| Typed into Safari's address bar, app doesn't open | By design: direct navigation in the browser never opens the app | Test from Notes or Messages on a real iPhone |
| Tapped on your own site, stays in Safari | Same-domain links continue in the browser | Use a different subdomain for links that should open the app |
| Fixed the server, still broken | The device still has the old file from Apple's CDN | Reinstall the app, or use ?mode=developer while testing. There's no CDN invalidation. |
| Domain has a port | applinks:example.com:8080 isn't supported |
Remove the port |
| Some paths fail | Path excluded or not matched in components, or old and new AASA formats mixed |
Use one format. Test with swcutil verify (below). |
| App opens, wrong screen | Routing bug, often cold start vs. warm start | Test both scene entry points |
Web browsers decide for themselves whether to support Universal Links, and support may vary in third-party browsers.
How do I check my AASA file?
Run these checks from a Mac instead of relying on an online AASA validator. All of them come from Apple TN3155.
- Fetch it and read the headers:
curl -v https://example.com/.well-known/apple-app-site-association
You want a 200 with JSON and no 301 or 302. Repeat with a random user agent (curl -A "MyAgent-Bot/*" …), because your server should accept any user agent.
- Download it the way the system does, then check it against a URL:
sudo swcutil dl -d example.com
sudo swcutil verify -d example.com -j ./example.json -u https://example.com/buy/42
A matching path returns Pattern "…" matched. An excluded path returns blocked match.
- Check the format. Use
appIDs(<Team ID>.<bundle ID>) with acomponentsarray. Don't mix it with the olderappIDandpathsformat (Apple: Supporting associated domains):
{
"applinks": {
"details": [
{
"appIDs": ["ABCDE12345.com.example.app"],
"components": [
{ "#": "no_universal_links", "exclude": true },
{ "/": "/buy/*" },
{ "/": "/help/website/*", "exclude": true },
{ "/": "/help/*", "?": { "articleNumber": "????" } }
]
}
]
}
}
Put exclude rules before the broader patterns they carve out, as Apple's example does.
-
On a real iPhone, turn on Developer Mode, go to Settings > Developer, turn on Associated Domains Development under Universal Links, then open Diagnostics and enter the full URL. It reports whether the link is valid for an installed app.
-
Still stuck? Take a sysdiagnose on the device and open
swcutil_show.txt. Find your App ID.Site/Fmwk Approval: approvedmeans Apple's CDN approved your file.User Approvalshows the person's app-or-browser choice.
Apple's CDN fetches the AASA file when the app is installed. Devices check for updates about once a week after that, and reinstalling the app gets the newest copy.
How do I set up the app side?
- Add the Associated Domains capability under Signing & Capabilities. Then add
applinks:example.com. Include only the host, with no path, no query and no trailing slash. A*.prefix matches subdomains. - Handle the incoming
NSUserActivitywithactivityType == NSUserActivityTypeBrowsingWeband readwebpageURL(Apple: Supporting universal links in your app). - If your app uses scenes, handle both entry points. When the app isn't running, the link arrives in
scene(_:willConnectTo:options:)throughconnectionOptions.userActivities. When the app is running or suspended, it arrives inscene(_:continue:). These are two separate test cases. - Validate every parameter and reject malformed URLs. Apple says to include improperly formatted URLs in your test cases.
How do you test Universal Links on a real iPhone?
The Simulator covers routing. Confirm the association and real taps on a real iPhone. A real iPhone covers how people actually tap links:
| Source app | App state | Expected |
|---|---|---|
| Messages | Not running (cold start) | App opens to the deep-linked screen |
| Backgrounded | App resumes to the deep-linked screen | |
| Notes | Logged out | Login, then continue to the target screen |
| Third-party app (chat or social) | Installed | App opens, or the link stays in that app's in-app browser. Record which. |
| Any | App not installed | Website handles the URL |
| Any | Excluded path | Website handles the URL |
Run the matrix on more than one iPhone model and iOS version, including iOS 27.
What is the difference between Universal Links and custom URL schemes?
Custom URL schemes such as myapp://item/42 need no website and no association. They also have no website fallback, and Apple notes that other apps can register the same scheme. Apple recommends Universal Links over custom schemes for this reason (Apple: Defining a custom URL scheme for your app). If you keep a scheme for backward compatibility, test it separately.
Android counterpart: Android App Links use android:autoVerify="true" and a /.well-known/assetlinks.json file. You can check verification state with adb shell pm get-app-links (Android: Verify App Links). See our Android deep links guide.
How does Mobot test Universal Links?
Mobot runs your link tests on real iPhones and Android phones, tapped by physical robots, so the link is opened from Messages, Mail and other apps the way a person would open it. Mobot AI and Mobot's team build your test cases from your link list and the screens each link should land on. When a link opens the wrong place, the defect report includes device screenshots of what happened.
FAQ
Can you test Universal Links in the iOS Simulator?
Yes. Install your app on a booted simulator and run xcrun simctl openurl booted "<your URL>". The Simulator may open https:// links in Safari or show an "Open in app?" prompt even when the association works, so confirm on a real iPhone by tapping the link from Messages or Mail.
Why are Universal Links not working?
The usual causes are a missing or redirected AASA file on that exact host, a mismatch between the applinks: entitlement and the domain, a path that isn't matched in components, or a device where someone chose to open that domain in the browser.
How do I check my AASA file?
Run curl -v against https://<domain>/.well-known/apple-app-site-association and check for a 200 with no redirect. Then use sudo swcutil dl -d <domain> and sudo swcutil verify on a Mac to test specific URLs against the file.
What is the difference between Universal Links and custom URL schemes? Universal Links are verified HTTPS links that fall back to your website. Custom schemes are not verified, have no website fallback, and other apps can register the same scheme.