# Testing Proximity Without an iPhone This document explains **how to run and test the entire app using only the iOS Simulator** — no physical iPhone required for the core interaction loop. ## The strategy in one sentence > **The interaction logic is fully testable in the simulator. Only the physical > sensors (UWB, NFC) need real hardware.** We achieved this by abstracting all hardware behind protocols (`ProximityAdapterProtocols`) and injecting a `SimulatedAdapter` in DEBUG builds. `PresenceEngine` talks to the protocols, never to hardware directly, so it behaves identically whether it's sensing real UWB or receiving simulated encounters. ## What you can test without any device | Capability | Simulator | Real iPhone | |---|---|---| | SwiftUI UI, navigation, chat | ✅ | ✅ | | Auth (Apple / Google) | ✅ (Apple needs bundle config) | ✅ | | Backend + WebSocket live loop | ✅ | ✅ | | Full reveal flow (wave → mutual → chat) | ✅ | ✅ | | Simulated encounters / crowd | ✅ | ✅ | | Core Location / iBeacon | ⚠️ simulated location | ✅ | | BLE broadcast/discovery | ⚠️ limited | ✅ | | **UWB (Nearby Interaction)** | ❌ | ✅ | | **NFC tap** | ❌ | ✅ | --- ## Quick start (simulator) ### 1. Run the reference backend The Swift client in DEBUG mode points at a local server. Start it: ```bash cd server npm install npm run dev # runs on http://localhost:8080 ``` ### 2. Run the app in the Simulator 1. Open `Proximity.xcodeproj` in Xcode. 2. Select an iPhone simulator (any model). 3. Build & run (⌘R). In DEBUG builds, `AppState` automatically: - Points `APIClient` at `http://localhost:8080` - Injects `SimulatedAdapter` into the `PresenceEngine` ### 3. Exercise the interaction loop 1. **Sign in** (Apple or Google — the reference server accepts any token). 2. Tap the big **"I'm Here"** toggle to go present. 3. Open the **debug menu** (hammer icon, top-left). 4. Tap **"Simulate someone walking by"** — a silhouette appears in the feed. 5. Tap **Wave** on the silhouette. 6. To complete the reveal, simulate the *other* side waving back — in real life the server pushes this over the socket. For a fully local test, you can drive the mutual wave via the debug tools or a second client. ### 4. Test the reveal moment When both sides wave, `RevealView` presents full-screen — two avatars converging. Tap **"Say hello"** to open the E2E chat. --- ## Driving the simulation The debug menu (`DebugMenu`) provides: - **Simulate someone walking by** — one UWB encounter. - **Simulate a crowd (20 people)** — a burst of silhouettes. - **Auto-broadcast nearby users** — emits encounters on a timer (a "busy street" simulator). `SimulatedAdapter` also exposes programmatic methods if you want to write UITests: ```swift simulatedAdapter.simulateEncounter(remoteToken: "any-token", distance: 1.0) simulatedAdapter.simulateCrowd(count: 20) ``` --- ## Two-simulator / two-client testing To test the **mutual** flow (both sides), run **two simulator instances**: 1. Boot two simulators (e.g. iPhone 15 + iPhone 15 Pro). 2. Run the app on both against the same local backend. 3. In each, go present and simulate an encounter (the debug menu gives each a distinct token). 4. Wave from one — the server derives each sender's own token from its session, records the directional edge, and when both directions exist it pushes `mutual` to **both** sockets. The reveal triggers on both devices in real time. The reference server now implements the full session→token mapping, so the mutual reveal and E2E key exchange work end-to-end across two clients. ## Running the UI tests The `ProximityUITests` target drives the happy path automatically: ```bash xcodebuild test \ -project Proximity.xcodeproj \ -scheme Proximity \ -destination 'platform=iOS Simulator,name=iPhone 15' \ -only-testing:ProximityUITests/ProximityFlowUITests ``` Start the reference server first (`cd server && npm run dev`). --- ## Testing on real hardware (the sensors) When you're ready to test UWB / BLE / NFC for real: 1. **Disable the simulated adapter.** Either build a non-DEBUG configuration, or set `AppState.isSimulated = false`. 2. Use two physical iPhones (iPhone 11+ for UWB). 3. The real `UWBAdapter`, `BLEAdapter`, `BeaconAdapter` take over. The rest of the app (auth, chat, reveal, backend) works identically — that's the point of the abstraction. --- ## What the simulator can't do (and why it's OK) - **UWB**: no support in the simulator. But UWB is only the *"this exact person"* distance sensor — the interaction logic (handshake → reveal) is fully testable via simulation. - **NFC**: no support. NFC is a niche "tap to connect" feature, not core. - **BLE**: very limited in simulator. The presence *logic* is testable; the radio isn't. Since the make-or-break mechanic — **the physical interaction moment** — is driven by logic + UI, not by the radio, you can validate the entire product experience in the simulator before ever touching real devices.