Test target piRemoteUITests now covers 8 features across 7 files (~73s total).
UI tests:
- SmokeUITests: app launches
- PairingUITests: launchUnpaired→PairingFlow, deep-link auto-pair
- StatusBarUITests: three icon buttons (Switcher/Settings/Unpair)
- SessionSwitcherUITests: list, select, create-with-unique-name, cleanup
- SettingsUITests: open sheet, Face-ID toggle flip, content checks
- ModifierBarUITests: button presence, paste sheet
- LockScreenUITests: lock overlay via --force-lock
App-source changes for testability (all dev/uitest-only):
- AppState: --reset-state / --enable-faceid / --force-lock launch args
- piRemoteApp: --pair-with-url launch arg auto-triggers .onOpenURL
- MainTerminalView: --uitest mode replaces SwiftTerm with static
placeholder and skips WS connection to keep app idle for XCUI
- TerminalViewController.feed: defensive guard against nil terminalView
Accessibility identifiers added:
- StatusBar buttons: accessibilityLabel + identifier
- ModifierBar BarButton/RepeatingBarButton: accessibilityLabel + .isButton
- SessionSwitcher '+' button
Tooling notes:
- cliclick workaround replaced by simctl privacy grant pasteboard
- Tests pre-fetch fresh pair-token from /pair-qr each run
- SwiftUI Toggle inside Form needs coordinate tap (not .tap())
Bug fixes uncovered while writing tests:
- Sidecar POST /sessions response was missing 'state' field
(now returns { id, name, state, lastOutputAt } to match GET).
iOS SessionItem decoder hardened to default state='idle' when missing.
|
||
|---|---|---|
| Sources | ||
| Tests/CoreTests | ||
| UITests | ||
| docs | ||
| piRemote.xcodeproj | ||
| .gitignore | ||
| README.md | ||
| project.yml | ||
README.md
pi-remote iOS
Native iOS app for the pi-remote-control sidecar.
Requirements
- Xcode 16.4+
- iOS 17.0+ device
- pi-remote-control sidecar running (Phase 1)
Building
# Open in Xcode (first time: Xcode downloads device support files automatically)
open piRemote.xcodeproj
# Or via CLI (after first Xcode open with device connected):
xcodebuild build -project piRemote.xcodeproj -scheme piRemote \
-destination "platform=iOS,name=<YourPhone>" \
CODE_SIGNING_STYLE=Automatic DEVELOPMENT_TEAM=KNXX8R3648
Project structure
Sources/
├── App/ — @main entry, ContentView, Assets
├── Core/
│ ├── Network/ — WebSocketClient, FrameCodec, ResumeCursor, TLS pinning
│ ├── Auth/ — Keychain, Pairing (QR exchange)
│ ├── Sessions/ — SessionRegistry, SessionConnection, PreConnectPool
│ ├── Push/ — APNs NotificationDelegate, DeviceTokenRegistrar
│ └── Persistence/ — ScrollbackCache, Preferences
└── UI/
├── Terminal/ — SwiftTerm wrapper, themes, fonts
├── Input/ — ModifierBar, sticky Ctrl, paste sheet
├── Status/ — StatusBar (pi state)
├── Sessions/ — SessionSwitcher
├── Pairing/ — QR scanner flow
└── Settings/ — Face-ID gate, sidecar info
Dependencies (via SPM)
- SwiftTerm — terminal emulator
- Starscream — WebSocket client
Apple Developer setup (one-time)
- In Apple Developer Portal:
- Create App ID:
de.vpsj.pi-remote, enable Push Notifications - Generate an APNs Auth Key (
.p8) — download once, keep safe - Note your Key ID and Team ID (
KNXX8R3648)
- Create App ID:
- Copy
.p8key to~/.local/share/pi-remote/apns/AuthKey_<KeyID>.p8 - Update
[apns]section in pi-remote-control config
Status
Phase 2 — in progress.
| Task | Status |
|---|---|
| T-2.0 Xcode scaffold | ✅ done |
| T-2.1 WebSocketClient + FrameCodec + ResumeCursor | ✅ done |
| T-2.2 Pairing + Keychain + QRScanner | ✅ done |
| T-2.3 TerminalView + ThemeStore + FontStore | ✅ done |
| T-2.4 ModifierBar + ModifierState + PasteSheet | ✅ done |
| T-2.5 SessionConnection (IC-2.1) + ScrollbackCache | ✅ done |
| T-2.9 APNs NotificationDelegate + DeviceTokenRegistrar | ✅ done |
| T-2.6 SessionRegistry + SessionSwitcher UI | ⛔ next |
| T-2.7 PreConnectPool | ⛔ after T-2.6 |
| T-2.8 StatusBar + Side-channel | ⛔ next |
| T-2.10 Background lifecycle | ⛔ todo |
| T-2.11 Face-ID gate + Settings | ⛔ next |
| T-2.12 TestFlight pipeline | ⛔ todo |
| T-2.13 MVP smoke test | ⛔ last |
App runs on device: iOS 26, wireless, Xcode 16.4 Intel Mac.
111 unit tests across FrameCodec, ResumeCursor, Pairing, Keychain, Theme, ModifierState, ScrollbackCache, DeviceTokenRegistrar.
See pi-remote-control/docs/PHASE-2-ios-mvp.md for full spec.