# The iOS app

Source: https://heyparlour.app/docs/ios

> A client in your pocket, with HomeKit, QR pairing, local discovery and a model on the phone.

The iOS app puts Parlour on the phone you already carry. It records, sends the
recording home and plays the answer back, just as a satellite does. Everything
that thinks, holds a key or touches the house still runs on the Mac at home.
The phone adds HomeKit, which it knows about without any setup, and a small
model of its own for when the house cannot be reached.

The app has three tabs: **Talk**, where you hold the button and speak;
**House**, your HomeKit rooms and accessories; and **Settings**, which server,
which room and what the app is allowed to do.

The app lives in `apps/ios` and uses only the public routes in
[Clients](/docs/clients): `/health` to check, `/v1/models` to confirm the
token, `/voice` for a recording and `/ask` for text. Anything the app does, you could build against those routes
too.

## Building it

The Xcode project is generated from `apps/ios/project.yml` instead of being
checked in, because nobody can review a `.pbxproj`.

```sh
brew install xcodegen
pnpm exec nx run ios:app          # generate the project and open it
pnpm exec nx run ios:xcodebuild   # compile for the simulator
pnpm exec nx run ios:xcodetest    # run ParlourTests
```

Parlour for iOS is not on the App Store yet, so for now you build it yourself.
It needs iOS 18 or newer. Signing is up to you. Set `PARLOUR_DEVELOPMENT_TEAM`
before generating, or pick a team in Xcode once. HomeKit and the local network
both need a real device, not the simulator.

## The first run

A fresh install walks you through setup before it shows the talk button, in
the same order and words as the menu bar app, so it takes about a minute with
the Mac nearby:

1. **Connect.** Scan the pairing code. The servers Bonjour found, and typing an
   address and token by hand, are folded away until you ask. The app checks
   the server there and then, so a wrong token shows up now rather than on
   your first question.
2. **Voice.** Allow the microphone, and say which room the phone usually lives
   in, so "lights off" means the lights in there.
3. **Extras.** Answering on the phone when home is out of reach. A phone that
   cannot run the on-device model skips this step.
4. **Finish.** What was set.

Settings has **Run setup again** at the bottom. A phone that was already
paired before setup existed counts as set up.

## What it asks for

You should know what a voice assistant is allowed to do before you trust it.
So the Settings tab lists the four permissions it relies on, with a mark
against each and an Ask button for any not yet granted, and every prompt says
what the permission gives you.

| Permission | What it gives you | Asked for when |
| --- | --- | --- |
| Microphone | Recording while you hold the talk button | The Voice step of setup, or the first time you hold the button |
| Local network | Finding the server with Bonjour, then talking to it | The app opens |
| HomeKit | The rooms and accessories on the House tab | You open that tab |
| Speech recognition | Understanding a request on the phone when the server cannot be reached | The phone first has to answer by itself, with the on-device answer switched on |
| Camera | Scanning the pairing code on the Mac | You tap **Scan pairing code**, in setup or in Settings |

HomeKit and multicast networking are entitlements as well as prompts. Apple
grants multicast to the team, so a build signed with another team needs it
left out of `Parlour.entitlements` or its own grant. If you say no to the
local network, iOS does not ask again, so Settings and setup offer
**Open Settings**, where the Local Network switch is. `NSAllowsLocalNetworking`
allows plain HTTP to the house, and only on the local network, because the
server sits on a private address and speaks HTTP. The phone page makes the
same trade. It is also why a reverse proxy is worth having if you want to
reach the house from outside.

## Pairing with your Mac

The quickest way to connect is a QR code. Run `parlour pair` on the Mac, or
open **Pair a phone** on the menu bar app's Status tab, then tap
**Scan pairing code** in the app's Settings. The Camera app reads the code too
and hands it to Parlour. One code carries the server's address and its access
token together, so the two can never come from different servers. The token
goes into the phone's keychain, never into a backup, and Settings checks the
server straight away.

A pairing link can also arrive from outside the app: from the Camera, or from
any web page or message with a `parlour://pair` link in it. So before taking
one, the app asks whether to pair with the server it names and shows the
address everything you say would go to. Only pair with a code your own server
showed you.

## Finding the server

Without a code, the app finds the server the same way a satellite does. It browses for
`_parlour._tcp`. Bonjour returns a service rather than an address, so the app
opens a connection to the service and reads the host and port off the
resolved path. Anything it finds appears in Settings.

An address you type in, pick from that list or take from a pairing code wins
over anything found. From then on, it is the only server the app talks to,
just as a satellite pins the first server it trusts. You can also name the
room the phone is in, so a follow-up lands in the right one.

Browsing is also what raises the local network prompt. So the prompt appears
as the app opens, not while someone is trying to say something.

## Managing the server

The Server tab manages the Mac from the phone, over the routes in
[Managing the server](/docs/clients#managing-the-server):

- **Model servers.** Whether the local model and whisper are running, with
  buttons to start, stop and restart them. Stopping one asks first, because
  the house cannot answer the same way without it.
- **Pipeline.** How many requests run at once, how many can wait per room,
  how many tasks one request can become, when to give up, and whether to read
  requests first. Saving restarts the agent so the change takes effect, and
  the steppers stop at the same limits the server enforces.
- **Maintenance.** Runs the doctor, shows the end of any of the three logs,
  and restarts the agent.

The server has the final say on every change and explains any refusal in its
own words. For example, it refuses to start a model that is too big for the
Mac, or to restart something that was restarted a moment ago.

## The model on the phone

The app can answer on its own using Apple's Foundation Models, which need
iOS 26 and Apple Intelligence switched on. It is off by default, and it is a
fallback, not a first stop. The phone has no tools, so it cannot switch
anything on, read a sensor or look anything up. The phone defers to the house
for the same reason the house tries its local model first.

When it is on and the server cannot be reached, the phone transcribes the
recording with on-device speech recognition and answers it on the device. The
answer is labelled as coming from the phone, not from home. Nothing in that
path leaves the handset. When the model is unavailable, Settings says why:
too old an iOS, a phone without it, Apple Intelligence switched off, or the
model still downloading.

## HomeKit

The House tab shows the homes, rooms and accessories HomeKit already knows
about, and can switch anything with a power state. It is deliberately not how
Parlour controls the house. The agent's tools go through Home Assistant on the
server, the one place that knows what is connected to what. The House tab is
a second opinion in your pocket, and a screen that still works when the Mac
at home is off.
