Proximity β€” Testing Guide

Step-by-step walkthrough for an Apple Silicon (M-series) MacBook Β· iOS Simulator Β· no physical iPhone needed for the core flow
Contents
  1. What you're testing & the big idea
  2. Prerequisites (install these first)
  3. Project structure map
  4. Start the reference backend
  5. Open & configure the project in Xcode
  6. Run the app in the Simulator
  7. Walk through the interaction loop
  8. Two-simulator mutual reveal test
  9. Run the automated UI tests
  10. Testing on a real iPhone (the sensors)
  11. Troubleshooting on M-series

1 What you're testing & the big idea

Proximity is a social app where you can only connect with people you're physically near. The make-or-break mechanic is the interaction moment: silhouette β†’ wave β†’ mutual reveal β†’ chat.

The key insight: All the interaction logic β€” the part that makes or breaks the community β€” is fully testable in the iOS Simulator, with no iPhone. Only the physical sensors (UWB, NFC) need real hardware. We built a SimulatedAdapter that stands in for the hardware, so the app behaves identically.
CapabilitySimulatorReal iPhone
UI, navigation, chatβœ…βœ…
Auth (Apple / Google)βœ…βœ…
Backend + WebSocket live loopβœ…βœ…
Reveal flow (wave β†’ mutual β†’ chat)βœ…βœ…
Simulated encounters / crowdβœ…βœ…
BLE broadcast / discovery⚠️ limitedβœ…
UWB (Nearby Interaction)βŒβœ…
NFC tapβŒβœ…

2 Prerequisites

Install these before starting. On an M-series Mac, everything runs natively on Apple Silicon.

A. Xcode (required)

Install from the Mac App Store, or run:

xcode-select --install
# If you need a specific version, use Xcodes:
brew install --cask xcodes

You need Xcode 15 or later for Swift 5.9+ concurrency and SwiftData. Xcode 16 recommended.

B. Node.js (required for the reference server)

Check if you have it:

node --version   # want v18+
npm --version

If not, install via Homebrew (native arm64 build):

brew install node

C. Homebrew (recommended)

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
M-series note: Everything here is native arm64. You do not need Rosetta for this project. If you ever see "running under Rosetta" in Activity Monitor for a tool, it's optional and not required.

3 Project structure map

Here's what you're working with:

Nearfield_Friends/
β”œβ”€β”€ Proximity/                  # iOS app (SwiftUI)
β”‚   β”œβ”€β”€ App/                    # entry point, AppState wiring
β”‚   β”œβ”€β”€ Models/                 # Encounter, Connection, DistanceTier…
β”‚   β”œβ”€β”€ Services/
β”‚   β”‚   β”œβ”€β”€ Proximity/          # PresenceEngine + adapters + SimulatedAdapter
β”‚   β”‚   β”œβ”€β”€ Network/            # APIClient, RealtimeClient (WebSocket)
β”‚   β”‚   β”œβ”€β”€ Auth/               # Apple/Google sign-in, profile import
β”‚   β”‚   └── Security/           # tokens, E2E crypto
β”‚   β”œβ”€β”€ ViewModels/             # Presence, Chat, Reveal
β”‚   └── Views/                  # SwiftUI screens + DebugMenu
β”œβ”€β”€ ProximityUITests/           # automated UI tests
β”œβ”€β”€ Proximity.xcodeproj/        # Xcode project (generated)
β”œβ”€β”€ scripts/
β”‚   └── generate_xcodeproj.py   # regenerates the .xcodeproj
β”œβ”€β”€ server/                     # reference backend (Node/TS)
β”‚   └── src/index.ts
└── docs/
    β”œβ”€β”€ backend-api-spec.md     # API contract
    β”œβ”€β”€ TESTING.md              # testing notes
    └── TESTING_GUIDE.html      # this file
Good news: A Proximity.xcodeproj is now included in the repo (generated by scripts/generate_xcodeproj.py). It contains the app target, the UI test target, and a shared scheme β€” so you can open it directly in Xcode and run xcodebuild test with no manual setup. If you ever add/remove Swift files, re-run the generator to keep the project in sync.

4 Start the reference backend

The Swift app in DEBUG mode points at a local server on localhost:8080. Start it first.

Open Terminal and run:

cd ~/path/to/Nearfield_Friends/server
npm install
npm run dev

You should see:

Proximity reference server on :8080
Keep this terminal window open. The server must stay running while you test. If you close it, the app will still work for local UI, but the live socket (mutual reveal, real-time chat) won't.
Quick sanity check: In a second terminal, run curl http://localhost:8080/v1/nearby?geohash=abc&tier=1 β€” you should get [] back.

5 Open & configure the project in Xcode

5a. Open the project

The Proximity.xcodeproj is already generated. Open it:

open Proximity.xcodeproj

or double-click it in Finder. It includes the app target, the ProximityUITests target, and a shared scheme, so everything is ready to go.

The entry point is ProximityApp.swift (marked @main).

Keeping the project in sync: If you add or remove Swift files, regenerate the project so the file list stays correct:
python3 scripts/generate_xcodeproj.py
Then re-open the project in Xcode.

5b. Set the signing team (free account is fine)

  1. Select the Proximity target in the project navigator.
  2. Go to Signing & Capabilities.
  3. Check "Automatically manage signing".
  4. Pick your Team (your Apple ID β€” free accounts work for the simulator).
  5. Set a unique Bundle Identifier, e.g. com.yourname.proximity.

5c. Add required capabilities (for real devices later)

For the simulator you only strictly need these for the real-hardware build, but add them now so the project is ready:

The simulator ignores UWB/NFC but will use simulated location.

5d. Add the Info.plist usage strings

Add these to Info.plist so permissions work:

NFCReaderUsageDescription   "Proximity uses NFC to connect when you tap phones."
NSBluetoothAlwaysUsageDescription "Proximity uses Bluetooth to detect nearby friends."
NSLocationWhenInUseUsageDescription "Proximity uses your location to find people nearby."

6 Run the app in the Simulator

  1. At the top of Xcode, pick a simulator from the device dropdown (e.g. iPhone 15 or iPhone 15 Pro).
  2. Press ⌘R to build & run.
  3. The first build may take a minute. The Simulator window opens with the app.
What happens automatically in DEBUG builds:
If the app can't reach the server: the simulator shares your Mac's network, so localhost works. If you see connection errors, make sure the server from Step 4 is still running, then use the debug menu's "Reconnect socket".

7 Walk through the interaction loop

This is the heart of the product. You'll simulate a full encounter with no hardware.

Step 1 β€” Sign in

Tap Continue with Google (or Apple). The reference server accepts any token, so this succeeds immediately. You'll land on the main screen.

Step 2 β€” Go present

Tap the big circular "Tap to be Present" button. It turns green and reads "I'm Here". This opens the live socket.

Step 3 β€” Simulate an encounter

  1. Tap the hammer icon (top-left) to open the Debug menu.
  2. Tap "Simulate someone walking by".
  3. Go back. A silhouette ("Someone nearby") appears in the feed.

Step 4 β€” Wave

Tap Wave on the silhouette. It changes to "Waved βœ“" β€” you're now waiting for the other person.

Step 5 β€” Complete the reveal (two ways)

Option A β€” two simulators (recommended, see next section): the server relays the mutual wave and the reveal triggers automatically.

Option B β€” single simulator: the reveal needs the other side to wave. Use the debug menu's "Simulate a crowd" to generate more silhouettes, or drive the mutual wave programmatically via the simulated adapter.

Step 6 β€” The reveal moment

When both sides wave, RevealView presents full-screen: two avatars converge. Tap "Say hello" to open the E2E-encrypted chat.

Step 7 β€” Chat

Type a message and send. In a two-simulator setup, the other side receives it decrypted in real time over the WebSocket.

8 Two-simulator mutual reveal test

This tests the real-time mutual flow end-to-end across two "people".

  1. Make sure the reference server is running (Step 4).
  2. In Xcode, boot a second simulator: File β†’ Open Simulator, or use βŒƒβŒ˜R after adding a second scheme destination.
  3. Run the app on both simulators (e.g. iPhone 15 + iPhone 15 Pro).
  4. In each, sign in and go present.
  5. In each, use the debug menu to simulate an encounter (each gets a distinct token).
  6. 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.
  7. The reveal triggers on both devices in real time.
Tip: To run two instances easily, create a second scheme (Product β†’ Scheme β†’ New Scheme) and set a different simulator destination, then run both schemes.

9 Run the automated UI tests

The ProximityUITests target drives the happy path automatically.

  1. Ensure the server is running.
  2. In Xcode, select the ProximityUITests test target.
  3. Press ⌘U to run all tests.

Or from the terminal:

cd ~/path/to/Nearfield_Friends
xcodebuild test \
  -project Proximity.xcodeproj \
  -scheme Proximity \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  -only-testing:ProximityUITests/ProximityFlowUITests
Ready to go: The ProximityUITests target and its shared scheme are already part of Proximity.xcodeproj, so ⌘U and the xcodebuild test command work out of the box.

10 Testing on a real iPhone (the sensors)

When you're ready to test the actual UWB / BLE / NFC radios:

  1. Disable the simulated adapter. Build a Release (non-DEBUG) configuration, or set AppState.isSimulated = false.
  2. Use two physical iPhones (iPhone 11+ for UWB).
  3. Point the app at a reachable backend (change APIClient.baseURL to your Mac's LAN IP or a deployed server).
  4. Trust the developer certificate on each device (Settings β†’ General β†’ VPN & Device Management).
  5. Run from Xcode with the device selected. The real UWBAdapter, BLEAdapter, BeaconAdapter take over.
Important: UWB and NFC are not available in the simulator. Only test these on real hardware. Everything else works identically β€” that's the point of the abstraction.

11 Troubleshooting on M-series

App won't connect to the server

βœ” Confirm the server terminal shows :8080 and is still running.
βœ” Use the debug menu's "Reconnect socket".
βœ” Try curl http://localhost:8080/v1/nearby?geohash=abc&tier=1 in Terminal.

Build fails / missing files

βœ” Make sure all folders under Proximity/ are added to the target (not just referenced).
βœ” Confirm ProximityApp.swift is the @main entry and only one @main exists.

Signing errors

βœ” For the simulator you can often use "Sign to Run Locally" / no team.
βœ” Use a unique bundle identifier.

"Rosetta" warnings or arch issues

βœ” This project is native arm64 β€” no Rosetta needed.
βœ” If a tool was installed via an Intel package, reinstall via brew (arm64) or use the Apple Silicon build.

Simulator can't find localhost

βœ” The simulator shares the Mac's network, so localhost resolves to your Mac. If you moved the server to another machine, update APIClient.local() to that IP.

Mutual reveal never triggers in single-simulator mode

βœ” A reveal needs both sides to wave. Use the two-simulator setup (Step 8) for the full end-to-end test, or drive the mutual wave programmatically.