Your Appium ferry-timetable deep link lands on the wrong screen — how do you diagnose it on Android and iOS?
answer
- delivery and destination are different failures
- the call returns before the screen does
- ask what is in the foreground now
- an unnamed target is resolved by the OS
- simulator and hardware take different paths
basics
~20 sEstablish which application received the URL before blaming the app. On Android read the foreground package and activity and check the call named package; on Apple platforms check bundleId, and whether simctl or WebDriverAgent delivered it.
solid answer
~40 sSplit the question into delivery and destination. Delivery: the command returns once the device has the URL, so a successful call proves nothing about the screen — assert with a wait on a destination element, never on the call returning. Destination: on Android the driver asks the system for an `android.intent.action.VIEW` intent, so if the call did not pass `package` the system resolved it, and an `https` URL will often go to a browser; read `mobile: getCurrentPackage` and `mobile: getCurrentActivity` to see what is actually in front. On Apple platforms confirm `bundleId` was supplied, and note that a Simulator opens the URL through `simctl` while a real device goes through WebDriverAgent, so the two targets fail differently. Only once the right app has the URL is it worth suspecting the app's own routing.
go deeper
Be ready to say that a deep-link call returning successfully is not the same as arriving, and that the first check is simply which application is now in front of you.
Be ready to explain why an unnamed target lets the operating system resolve the URL, and to name the Android and Apple arguments that pin it to the app under test.
Be ready to walk a triage order out loud: prove delivery, identify the foreground application, establish which delivery path ran, and only then suspect the app's own URL routing.
Be ready to decide how much of a suite should enter by URL at all, and how you keep that entry reliable across simulator and hardware lanes without every team inventing its own workaround.
## Separate two questions before touching anything A deep-link step that ends on the wrong screen has failed at one of two points, and they have nothing to do with each other: 1. **Delivery** — did the URL reach the operating system at all, and did the command return before anything rendered? 2. **Destination** — which application did the operating system hand the URL to? Most wasted triage time comes from conflating them. The command succeeding tells you delivery worked. It says nothing about destination, and destination is where the wrong-screen symptom almost always lives. ## Delivery: the call returns early, on both platforms On Android and on Apple platforms alike, the deep-link call returns once the device has been given the URL — not once your screen has finished building. That produces a specific and recognisable failure shape: - The call succeeds, the next find runs immediately, and it matches whatever is still on screen. - The test then reports a locator failure, pointing you at the locator rather than at the entry. - The same case passes on a fast device and fails on a slow one, which reads as flake. The fix is not a longer sleep. Follow the entry with an ordinary wait on an element that exists only on the destination screen, so the assertion is about arrival rather than about the command. On Android, `waitForLaunch` is sometimes reached for here and it is the wrong lever: it governs whether the driver waits for the started component to hand control back, not whether your content is ready. ## Destination on Android: who won the intent The Android driver asks the system, through `adb`, to start an `android.intent.action.VIEW` intent carrying the URL. If the call did not name `package`, the system resolves that intent against everything installed on the device. - For a custom scheme only your app claims, resolution usually lands correctly. - For an `https` URL the field is crowded and a browser is a very likely winner. - A device carrying a second build of the same app can send the URL to the wrong one. The cheap observation is what is in front right now: `mobile: getCurrentPackage` and `mobile: getCurrentActivity` tell you which application and component the system actually brought forward. If that is a browser, the diagnosis is finished and the fix is to pass `package` on the deep-link call. ## Destination on Apple platforms: which path opened the URL On Apple platforms the driver hands the URL to the operating system and the registered handler opens it, but the route to the OS depends on the target: - On a **Simulator** the driver goes through `simctl`. - On a **real device** it goes through WebDriverAgent. So one suite has two delivery paths, and a case that works locally on a Simulator can behave differently on hardware in a lane. Confirm that `bundleId` was supplied, so the URL is opened by the app under test rather than by whatever the system would otherwise pick, and establish which of the two targets the failing run used before comparing it with a passing one. ## A triage order that works | step | Android | Apple platforms | |---|---|---| | 1. was the target named | was `package` in the parameter map | was `bundleId` in the parameter map | | 2. what is in front now | `mobile: getCurrentPackage`, `mobile: getCurrentActivity` | which application the session is driving | | 3. which delivery path | `adb` on the host | `simctl` on a Simulator, WebDriverAgent on hardware | | 4. did the test wait | wait on a destination element | wait on a destination element | Work it top to bottom and stop at the first step that explains the symptom. Steps 1 and 2 close the large majority of these, because "the URL went somewhere else" is far more common than "the app routed it wrongly". ## What not to conclude - Do not conclude the URL is malformed because nothing visible happened; a scheme no installed app claims tends to fail quietly. - Do not conclude the driver is broken because the call succeeded and the screen did not change; that is the command's normal return behaviour. - Do not switch to tapping through the flow as a fix; that hides the resolution problem rather than answering it, and it resurfaces elsewhere. - Do not assume one platform's explanation transfers: an intent-resolution answer means nothing on Apple platforms, and a `simctl` answer means nothing on Android. ## Saying it well A senior answer is a sequence, not a fact: prove delivery, then prove destination, then and only then suspect the application's own routing — and give the Android and the Apple half of each step separately, because the mechanisms genuinely differ.
- The Android deep link opens a browser. What is the single most likely cause?The call did not name `package`, so the system resolved the `android.intent.action.VIEW` intent itself and an `https` URL was claimed by a browser. Passing `package` aims the intent at the app under test and removes the resolution question entirely.
- The same case passes on an Apple Simulator and fails on a real device. Where do you look first?At the delivery path. A Simulator opens the URL through `simctl` while a real device goes through WebDriverAgent, so the two targets fail differently. Confirm `bundleId` is set for both, then compare which application came forward on the failing target.
- Why is adding a sleep after the deep-link call a bad fix?Because it treats a timing symptom as the cause and hides the real question, which is which application received the URL. A wait on an element of the destination screen fixes the timing honestly and still fails fast, with a useful message, when the wrong app is in front.
saying these in an interview costs you the question
- Treating a successful deep-link call as proof the right screen opened
- Blaming the locator when the wrong application is in the foreground
- Adding a fixed sleep instead of waiting on a destination element
- Applying an intent-resolution explanation to an Apple platform failure
- Assuming a Simulator and a real Apple device deliver the URL the same way
- Reaching for waitForLaunch as a wait for the destination screen