# `mix mob.uninstall`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mix/tasks/mob.uninstall.ex#L1)

Uninstall a Mob app from one or more connected devices.

By default, uninstalls the **current project's** app from the
auto-detected device (when exactly one device is connected). For
other scopes, pass the relevant flag.

## Usage

    mix mob.uninstall                                    # this app, one device
    mix mob.uninstall --all-devices                      # this app, all emulators/sims (NOT phones)
    mix mob.uninstall --all-physical                     # this app, every physical device
    mix mob.uninstall --all-devices --all-physical       # literally everything
    mix mob.uninstall --device emulator-5554             # this app, one named device
    mix mob.uninstall --device foo --device bar          # this app, several
    mix mob.uninstall --all-apps                         # one device, every mob app
    mix mob.uninstall --all-devices --all-apps           # every mob app on every emulator/sim
    mix mob.uninstall --bundle-id com.x.y                # override the project's bundle_id
    mix mob.uninstall --bundle-prefix com.acme           # override the prefix for --all-apps
    mix mob.uninstall --yes                              # skip the confirmation prompt
    mix mob.uninstall --help                             # this help
    mix mob.uninstall -h                                 # this help

## Options

  * `--device <id>` — Target a specific device by serial or name.
    Repeat for multiple devices: `--device a --device b`. Works
    for any device type — `--device` is the explicit override
    that bypasses the emulator/physical filter.
  * `--all-devices` — Target every connected **emulator or
    simulator**. Physical devices are NEVER swept by this flag —
    that requires `--all-physical` or `--device <id>` explicitly.
    Rationale: emulators are disposable; physical devices are
    someone's personal phone, with potential to do real damage.
  * `--all-physical` — Target every connected physical device
    (iPhones over USB/Wi-Fi, Android devices via adb). Opt-in.
    Composes with `--all-devices` to mean "literally everything."
  * `--bundle-id <id>` — Uninstall this specific bundle id instead of
    auto-detecting the project's.
  * `--all-apps` — Match every installed package whose id starts with
    `bundle_prefix` (defaults to `MOB_BUNDLE_PREFIX` env or
    `com.example`). Useful for clearing stale test apps in one shot.
  * `--bundle-prefix <prefix>` — Override the prefix used by
    `--all-apps`. Defaults to `MobDev.Config.bundle_prefix/0`.
  * `--yes` — Skip the y/N confirmation prompt. The prompt is skipped
    automatically when targeting exactly one app on exactly one
    device (low blast radius).
  * `--help` / `-h` — Print this help text.

## Default scope behaviour

When you pass no flags:

  * 0 devices connected → exit with "no devices found"
  * 1 emulator/sim connected → auto-target it (physical devices
    are never the auto-target — they require explicit selection)
  * >1 devices or only physical devices → exit with an
    instruction to pass `--device`, `--all-devices`, or
    `--all-physical`. Never silently fans out without consent.

## Per-platform mechanics

  * **Android** — `adb -s <serial> uninstall <pkg>`. "Unknown
    package" responses bucket as skipped (the app wasn't installed),
    everything else non-zero is a failure.
  * **iOS simulator** — `xcrun simctl uninstall <udid> <bundle>`,
    followed by a probe through `simctl listapps` to distinguish
    actual uninstall from "wasn't installed in the first place".
  * **iOS physical device** — `xcrun devicectl device uninstall app
    --device <udid> <bundle>`. `ContainerLookupErrorDomain` in
    output → not installed (skipped). Requires the device to be
    paired and trusted via Xcode.

# `format_summary`

```elixir
@spec format_summary([MobDev.Uninstaller.result()], [MobDev.Uninstaller.result()], [
  MobDev.Uninstaller.result()
]) :: [String.t()]
```

Build the summary lines for an uninstall run. Pure — pinned by tests
in `test/mix/tasks/mob_uninstall_test.exs`. Public so the rendering
invariants (skipped never gets counted as failed, etc.) can be
asserted independent of connected hardware.

# `should_skip_prompt?`

```elixir
@spec should_skip_prompt?(
  MobDev.Uninstaller.plan(),
  keyword()
) :: boolean()
```

Decides whether to skip the y/N confirmation prompt before executing
a `plan`. Returns `true` when:

  * the user passed `--yes` (any truthy value at `opts[:yes]`), OR
  * the plan targets exactly one device with exactly one bundle id
    (low blast radius — destructive intent is unambiguous and the
    preview already showed exactly what's about to happen).

Pure for testability — the original inline `opts[:yes] or
single_target?` form crashed on `BadBooleanError` because
`opts[:yes]` is `nil` when the flag isn't passed and Elixir 1.20's
type checker enforces boolean operands on `or`. Pinning this as a
helper means the boolean-coercion mistake can't happen again.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
