iOS & Apple Watch

Cacophony ships a native iOS phone app (CacophonyCompanion) and an Apple Watch companion (CacophonyCompanionWatch) under companion/ios/. They target feature parity with the Android phone app and the Wear OS app using native SwiftUI / WatchKit idioms, and share a platform-agnostic Swift kit. The cleanroom rebuild is tracked under epic.

Components

PathWhat it is
Sources/CacophonyCompanionKit/Platform-agnostic Swift library: daemon connection config plus shared value types (DaemonConfig, Bead, Choice, DaemonEnvelope). No SwiftUI/UIKit/WatchKit imports, so it builds under the nixpkgs Swift toolchain alone. Shapes mirror the macOS CacophonyKit.
Sources/CacophonyCompanionKitSmoke/Nix-friendly executable that round-trip-tests the kit value types (XCTest ships only with Xcode, so this is the substitute test surface on nix-only hosts).
CacophonyCompanion/The SwiftUI iOS phone app.
CacophonyCompanionWatch/The Apple Watch app.
CacophonyComplication/Watch complications.
CacophonyShareExtension/Share image/file/url/text into the file cache or as quick-file beads.
CacophonyWidgets/WidgetKit widgets (on-device shipping is operator-gated on App Store / App Group provisioning).

Two build paths

The host fleet does not always have full Xcode, so there are two intentional entrypoints:

  1. Agent-verifiable SwiftPM kit (no Xcode). Builds and tests the shared CacophonyCompanionKit under the nixpkgs Swift toolchain — works on a macOS host with only the Command Line Tools. This is the lane CI / managed agents use to keep the shared logic green. Run it from the repo root with just ios-kit-smoke (or ./scripts/ios-kit-smoke.sh). The wrapper prefers the host Xcode toolchain on macOS and falls back to the nix Swift toolchain on nix-only hosts (the nix cc-wrapper breaks the Apple multi-target manifest compile, so the wrapper compiles the smoke with host Xcode swift under env -i).
  2. Xcode / Simulator app build. Generates the Xcode project from the committed XcodeGen project.yml and builds the SwiftUI app plus the Apple Watch app against a Simulator destination. Requires full Xcode and the iOS/watchOS Simulators, so it cannot run on a Command-Line-Tools-only host. See companion/ios/BUILD-XCODE.md.

The nix Swift toolchain can typecheck only the host macOS triple; there is no iPhoneSimulator SDK outside Xcode, so any real Simulator build, install, UI-drive, and screenshot QA needs Xcode.app on the macOS builder (an operator-privileged, license-gated install — a managed agent must not attempt it via sudo).

Connecting to a daemon

The companion reaches a real caco-daemon three ways:

iPhone Cluster overview: the node, agent and project counts, idle-agent warning and inspect links iPhone Agents: running agents grouped by state with node, project, bead and runtime iPhone project chat: agents' progress reports with the broadcast composer iPhone Beads: active beads with priority, tags and age
The iPhone app in the simulator, connected to a replay of the site's invented demo fleet.

Feature surface

The iPhone app matches Android, web, TUI and macOS:

Responses decode defensively: a malformed or missing list from the daemon shows as empty rather than blanking the screen.

Apple Watch

The watch app is a glanceable, Dynamic-Type-tolerant surface (42+ views) built to the WATCH-LAYOUT.md contract: List / NavigationStack / Section over absolute geometry, one title plus one compact meta line per row, short badges (3, 2!, P1) that never rely on color alone, and watch-native controls (Picker, Button, NavigationLink) since Menu is unavailable on watchOS. It runs in two connection modes: Relay via iPhone is the safe default and Direct Wi-Fi is explicit, with mode switches preserving stored direct credentials. Disconnection wording is specific: Phone disconnected when the phone is unreachable in Relay mode, Phone's daemon disconnected when the phone is reachable but its daemon link is not, and cached/stale state with age in Direct mode. The watch can resolve choices, run actions and crons (direct and relay), and refresh complications from live app-group data. An ActivityKit Live Activity also surfaces the active agent's live status in the watchOS Smart Stack (with Dynamic Island support), keeping the current agent glanceable without opening the app.

Distribution

App builds are signed with SOPS-encrypted Apple signing material (secrets/apple-signing.sops.yaml via scripts/with-signing-secret.sh) and ship through the TestFlight / App Store track. Widgets and other on-device extensions depend on operator-provisioned App Store and App Group entitlements. The agent-verifiable kit smoke (just ios-kit-smoke) is the no-Xcode green-keeping check for the shared logic.