skip to content

In Appium's mobile: deepLink, what names the target app on Android versus iOS?

level: middleimportance: must knowfreq 57%

answer

  1. the map differs, the method does not
  2. one names a package, one a bundle
  3. aiming an intent versus naming an app
  4. not capabilities, no appium: prefix
  5. package on Android, bundleId on Apple

basics

~20 s

Android's UiAutomator2 driver names the receiving app with package; Apple's XCUITest driver names it with bundleId. Both sit in the same parameter map beside url, and both exist to stop the operating system resolving the URL to some other app.

solid answer

~40 s

The command name is shared but the parameter map is not. On Android, UiAutomator2's `mobile: deepLink` takes `url` plus `package` — the package of the app that should receive the `android.intent.action.VIEW` intent the driver asks the system to start — and it also accepts `waitForLaunch`, which only controls whether the driver waits for the started component to hand control back. On Apple platforms, XCUITest's `mobile: deepLink` takes `url` plus `bundleId`, the bundle identifier of the app that should open the URL. Both arguments exist for the same reason: with no app named, the operating system resolves the URL itself, and an `https` URL in particular is very likely to land in a browser. These are execute-method arguments, not capabilities, so they carry no `appium:` prefix.

go deeper

for a junior

Be ready to say that the deep-link call takes the URL plus one more argument naming the app, and that the name of that argument is not the same on Android as on Apple platforms.

for a middle

Be ready to name both arguments and explain why each exists: package aims the android.intent.action.VIEW intent on Android, bundleId names the app that opens the scheme on Apple platforms.

for a senior

Be ready to describe how you configure both identifiers for one suite, and what you observe on a shared device when a URL is resolved by an application other than the one under test.

for a principal

Be ready to weigh whether entry URLs and app identifiers belong in test configuration or in a contract owned jointly with the app teams, and who pays when one of them changes.

## Why the argument exists at all Handing a device a URL is an ambiguous request. More than one installed application can claim the same URL, and the operating system, not your test, decides who gets it. Appium's deep-link command therefore lets you say which app should receive it. That single argument is what turns "open this URL somewhere" into "open this URL in the app under test", and it is spelled differently on the two platforms. ## The two parameter maps | | Android (UiAutomator2) | Apple (XCUITest) | |---|---|---| | the URL | `url` | `url` | | the target app | `package` | `bundleId` | | what that identifier is | the application's package name | the application's bundle identifier | | with no target named | the system resolves the view intent itself | the system's registered handler takes the URL | | extra argument | `waitForLaunch` | none of the same shape | Both maps are posted the same way — a script name plus one parameter map on `POST /session/:sessionId/execute/sync` — so from the client's point of view only the map changes. That is exactly why the divergence is easy to miss until a run fails on one platform and passes on the other. ## Android: naming the receiver of a view intent On Android the driver asks the system to start an `android.intent.action.VIEW` intent carrying the URL, reaching the device through `adb` on the host. What `package` does is aim that intent: - With no `package`, the system resolves the intent against every app that can handle the URL. - For a custom scheme that only your app claims, resolution usually still lands correctly. - For an `https` URL the field is crowded, and a browser is a very plausible winner. - With `package` set, the intent is aimed at that application, so the resolution question never arises. - `waitForLaunch` is about timing, not targeting: it controls whether the driver waits for the started component to hand control back before the command returns. A useful way to hold it: `package` decides **who** receives the URL and `waitForLaunch` decides **how long the command lingers**. Neither one waits for your screen's content. ## Apple platforms: naming the app that opens the URL On Apple platforms the driver hands the URL to the operating system and the registered handler opens it. `bundleId` names the application that should do the opening. The route to the OS is not the same on every target: - On a Simulator, the driver goes through `simctl`. - On a real device, it goes through WebDriverAgent. - Both paths end with the OS opening the URL, but they fail differently, which matters when the same suite runs on a Simulator locally and on hardware in a lane. As on Android, naming the app is the difference between a deterministic entry and one that depends on what else happens to be installed on that device. ## Two traps that catch mid-level candidates 1. **Treating these as capabilities.** `package` and `bundleId` here are arguments inside an execute method's parameter map. They are not session capabilities and carry no `appium:` prefix. A candidate who reaches for the capability names instead is describing a different mechanism at a different point in the session's life. 2. **Assuming one name covers both platforms.** Putting `package` into an Apple session's map, or `bundleId` into an Android one, produces a call the driver does not read as a target — and the confusing outcome is not an error but a URL that opens somewhere else, followed by a find that fails on an unrelated screen. ## What this means for a cross-platform suite The honest shape of a shared helper is: one URL, one per-platform identifier, one branch that builds the map. - Keep the deep-link URLs themselves in one place, since they are usually the same string on both platforms. - Keep the Android package and the Apple bundle identifier beside each other in configuration, because the helper needs whichever matches the running session. - Do not abstract the argument name away behind a single invented key; the divergence lives in the driver's signature, and hiding it only moves the failure later. - Always follow the call with a wait on an element of the destination screen, on both platforms. Said compactly in an interview: same method, same URL argument, different target argument — `package` on Android because the driver is aiming an intent, `bundleId` on Apple platforms because the driver is naming the app that should open the scheme.

  • What actually happens on Android if the package argument is left out?
    The driver still asks the system to start an `android.intent.action.VIEW` intent for the URL, but the system resolves it. A custom scheme only your app claims usually still lands correctly; an `https` URL is likely to be taken by a browser, so the session ends up driving the wrong application entirely.
  • Are package and bundleId here the same thing as the session capabilities that name an app?
    No. These are arguments inside the execute method's parameter map, sent on `POST /session/:sessionId/execute/sync` after the session exists, and they carry no `appium:` prefix. Capabilities configure the session at creation time; this argument targets one deep-link call.
  • Does waitForLaunch make the deep-link call wait for the destination screen?
    No. On Android it only controls whether the driver waits for the started component to hand control back before the command returns. It says nothing about your content, so the call is still followed by an ordinary wait on an element of the destination screen.

saying these in an interview costs you the question

  • Using package in an Apple session's parameter map or bundleId in an Android one
  • Calling these session capabilities and prefixing them with appium:
  • Claiming the URL always reaches your app even with no target named
  • Saying waitForLaunch waits for the destination screen to render
  • Assuming an https deep link behaves the same as a custom scheme on Android