# `MobDev.IOSInstalls`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/ios_installs.ex#L1)

A record of which apps `mob_dev` has installed on which physical iOS device.

This exists for exactly one reason: so that `mob_dev` can clear other Mob
apps off a device without touching anything else on it.

Physical-device Mob apps each start an in-process EPMD bound to
`0.0.0.0:4369` (`mob/ios/mob_beam.m`), so only one can run at a time —
launching a second gets EADDRINUSE and no BEAM. Clearing the others before
launch is therefore necessary. What is *not* necessary, and what this module
exists to prevent, is deciding which processes to kill by pattern-matching
the running process list: every third-party app on the phone runs from
`/private/var/containers/Bundle/Application/`, so that pattern matches
Spotify, a banking app and everything else the owner had open (MOB-70).

A device is someone's phone. `mob_dev` gets to kill what `mob_dev` put there,
and nothing else. If this record is missing or empty, the correct behaviour
is to kill nothing.

Entries are dropped on uninstall (`forget/2`). Since the process listing
carries no bundle ids, matching is by `.app` name — so a stale entry is not
inert: it would stay killable, and a third-party app that later took that
name would inherit it.

Stored as JSON at `~/.mob/ios_installs.json`, keyed by device UDID. It is a
cache, not a source of truth: deleting it costs a stale Mob app surviving a
launch, not correctness.

# `app`

```elixir
@type app() :: %{bundle_id: String.t(), app_name: String.t()}
```

# `forget`

```elixir
@spec forget(String.t(), String.t()) :: :ok
```

Drop `bundle_id` from `udid`'s record, after uninstalling it.

Matching is by app NAME, so a stale entry is not inert: it stays killable
for ever, and a third-party app that later takes that name inherits the
entry. Forgetting on uninstall is what makes the module's promise — we kill
what we put there — true rather than approximately true.

# `installed`

```elixir
@spec installed(String.t()) :: [app()]
```

Apps `mob_dev` has installed on `udid`, newest first. `[]` when unknown —
which callers must treat as "kill nothing", not "kill everything".

# `path`

```elixir
@spec path() :: Path.t()
```

Path to the registry file. Override with `MOB_IOS_INSTALLS` in tests.

# `record`

```elixir
@spec record(String.t(), String.t(), String.t()) :: :ok
```

Record that `bundle_id` (whose bundle is `app_name`.app) is installed on
`udid`.

Idempotent: re-installing the same app does not duplicate the entry, and a
changed `app_name` for a known bundle id replaces it rather than accumulating.

---

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