CalcSnippets
Mobile Development 4 min read

Test iOS Deep Links With `simctl openurl` and a Fresh Install

A deep link can work only because the simulator already remembers app state. Test URL routing on a clean install and capture the exact URL and selected scheme.

Exercise the route through the operating system

Use simctl openurl to send a URL to a simulator:

xcrun simctl openurl booted 'myapp://orders/42?source=test'

This verifies that the simulator can dispatch the URL to an installed app handler. It does not by itself prove Universal Links association, domain verification, or that the app renders the intended screen.

Separate custom schemes from Universal Links

Custom URL schemes are registered in the app and can be claimed by another app. Universal Links require the app entitlement and a correctly hosted association file for the domain. Test each path independently. For Universal Links, verify the association file from the public host, the app's associated-domains entitlement, and the exact signed bundle identifier.

Use a fresh install or reset link state when the test depends on first-open behavior. Confirm the app's initial route, query parameter parsing, authentication redirect, and fallback for malformed URLs. A simulator with an old app version or cached association decision can give a false pass.

Log only safe route metadata; query strings can contain tokens or personal data. Add automated tests for URL parsing and a simulator smoke test for OS dispatch. When the OS opens the app but the screen is wrong, debug route mapping and app state rather than changing the associated-domain server configuration blindly.

For a reliable regression case, record the exact simulator UDID, installed build number, URL scheme, and whether the app was already running. Test cold launch and warm launch separately: an app can route correctly when resumed but ignore the same link during initial scene creation. Keep authentication tokens out of sample URLs and use a non-sensitive test order ID.

Treat deep links as input contracts

Test the scheme, host, path, query, encoding, and expected fallback separately. A URL that opens the app is not necessarily routed to the correct screen. Use xcrun simctl openurl with an explicit UDID when multiple simulators are active, and record the command without secrets. Verify cold launch, warm launch, background resume, and an already-open different route.

Check that the app validates identifiers and handles malformed or unsupported URLs without crashing. A deep link can arrive before dependency initialization or authentication state is ready, so the router should retain or reject it deliberately. Use a synthetic ID and a test account; never place a real access token in a URL, fixture, screenshot, or CI log.

For universal links or associated domains, simulator opening is only one part of validation. Confirm entitlements, domain association, and the installed build's bundle identity. Add a UI or integration test that asserts the final route and a telemetry event that records only a safe route category. This makes a routing regression observable without logging user data.

Test cold launch, warm launch, background resume, malformed URLs, unsupported routes, and an already-open different screen. Use a synthetic identifier and never place credentials in a sample URL or CI log. Validate encoding and query parsing separately from navigation. The app should reject unsafe input without crashing and make authentication or deferred routing behavior explicit.

Test custom schemes and Universal Links independently. A custom scheme proves OS dispatch, while a Universal Link also depends on the signed entitlement and public association file. Add parser tests for percent encoding, missing identifiers, duplicate parameters, and unknown routes. Keep query values synthetic and redact them from telemetry. Verify the installed bundle identity before interpreting a route failure.

Test a cold launch, warm launch, background resume, and already-open different screen. A route that works only when the process is alive may fail during scene initialization. Keep authentication and deferred navigation explicit, and never use real order IDs or tokens in sample URLs.

Test custom schemes and Universal Links independently. A custom scheme proves OS dispatch, while a Universal Link also depends on the signed entitlement and public association file. Add parser tests for percent encoding, missing identifiers, duplicate parameters, and unknown routes. Keep query values synthetic and redact them from telemetry.

Test cold launch, warm launch, background resume, malformed URLs, unsupported routes, and an already-open different screen. Use a synthetic identifier and never place credentials in a sample URL or CI log. Validate encoding and query parsing separately from navigation. The app should reject unsafe input without crashing and make authentication or deferred routing behavior explicit.

Keep reading

Related guides