514 lines
23 KiB
HTML
514 lines
23 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>Proximity — Testing Guide (M-Series Mac)</title>
|
||
|
|
<style>
|
||
|
|
:root {
|
||
|
|
--bg: #0f1115;
|
||
|
|
--panel: #171a21;
|
||
|
|
--panel-2: #1e222b;
|
||
|
|
--border: #2a2f3a;
|
||
|
|
--text: #e6e8ee;
|
||
|
|
--muted: #9aa3b2;
|
||
|
|
--green: #34c759;
|
||
|
|
--green-dim: #1f7a3d;
|
||
|
|
--blue: #4aa8ff;
|
||
|
|
--amber: #ffb340;
|
||
|
|
--red: #ff5f57;
|
||
|
|
--mono: "SF Mono", "Menlo", "Consolas", monospace;
|
||
|
|
}
|
||
|
|
|
||
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
|
|
||
|
|
body {
|
||
|
|
background: var(--bg);
|
||
|
|
color: var(--text);
|
||
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
|
|
line-height: 1.65;
|
||
|
|
padding-bottom: 80px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.wrap { max-width: 860px; margin: 0 auto; padding: 0 24px; }
|
||
|
|
|
||
|
|
header {
|
||
|
|
background: linear-gradient(135deg, #0d1f14 0%, #0f1115 60%);
|
||
|
|
border-bottom: 1px solid var(--border);
|
||
|
|
padding: 48px 0 36px;
|
||
|
|
margin-bottom: 32px;
|
||
|
|
}
|
||
|
|
header .wrap { display: flex; align-items: center; gap: 20px; }
|
||
|
|
.logo {
|
||
|
|
width: 64px; height: 64px; border-radius: 16px;
|
||
|
|
background: radial-gradient(circle at 30% 30%, #34c759, #1f7a3d);
|
||
|
|
display: flex; align-items: center; justify-content: center;
|
||
|
|
font-size: 30px; color: white; flex-shrink: 0;
|
||
|
|
box-shadow: 0 8px 24px rgba(52,199,89,.35);
|
||
|
|
}
|
||
|
|
h1 { font-size: 26px; font-weight: 700; letter-spacing: -0.5px; }
|
||
|
|
.sub { color: var(--muted); font-size: 15px; margin-top: 4px; }
|
||
|
|
|
||
|
|
h2 {
|
||
|
|
font-size: 21px; font-weight: 700; margin: 40px 0 16px;
|
||
|
|
padding-bottom: 8px; border-bottom: 1px solid var(--border);
|
||
|
|
display: flex; align-items: center; gap: 10px;
|
||
|
|
}
|
||
|
|
h2 .step-num {
|
||
|
|
background: var(--green); color: #07130a; font-size: 13px; font-weight: 800;
|
||
|
|
width: 26px; height: 26px; border-radius: 8px; display: inline-flex;
|
||
|
|
align-items: center; justify-content: center; flex-shrink: 0;
|
||
|
|
}
|
||
|
|
h3 { font-size: 16px; font-weight: 600; margin: 22px 0 10px; color: var(--text); }
|
||
|
|
|
||
|
|
p { margin: 10px 0; color: var(--text); }
|
||
|
|
.muted { color: var(--muted); }
|
||
|
|
|
||
|
|
.card {
|
||
|
|
background: var(--panel); border: 1px solid var(--border);
|
||
|
|
border-radius: 12px; padding: 18px 20px; margin: 14px 0;
|
||
|
|
}
|
||
|
|
.card.tip { border-left: 4px solid var(--blue); }
|
||
|
|
.card.warn { border-left: 4px solid var(--amber); }
|
||
|
|
.card.danger { border-left: 4px solid var(--red); }
|
||
|
|
.card.success { border-left: 4px solid var(--green); }
|
||
|
|
|
||
|
|
code {
|
||
|
|
font-family: var(--mono); font-size: 13px;
|
||
|
|
background: var(--panel-2); border: 1px solid var(--border);
|
||
|
|
padding: 2px 6px; border-radius: 5px; color: #d7e0ee;
|
||
|
|
}
|
||
|
|
|
||
|
|
pre {
|
||
|
|
font-family: var(--mono); font-size: 13px; line-height: 1.6;
|
||
|
|
background: #0b0d12; border: 1px solid var(--border); border-radius: 10px;
|
||
|
|
padding: 16px 18px; overflow-x: auto; margin: 12px 0;
|
||
|
|
}
|
||
|
|
pre code { background: none; border: none; padding: 0; color: #c9d4e6; }
|
||
|
|
.cmt { color: #5c6b7a; }
|
||
|
|
|
||
|
|
ul, ol { margin: 10px 0 10px 22px; }
|
||
|
|
li { margin: 6px 0; }
|
||
|
|
|
||
|
|
table { width: 100%; border-collapse: collapse; margin: 14px 0; font-size: 14px; }
|
||
|
|
th, td { text-align: left; padding: 10px 12px; border: 1px solid var(--border); }
|
||
|
|
th { background: var(--panel-2); font-weight: 600; }
|
||
|
|
td { background: var(--panel); }
|
||
|
|
.yes { color: var(--green); font-weight: 600; }
|
||
|
|
.no { color: var(--red); font-weight: 600; }
|
||
|
|
.part { color: var(--amber); font-weight: 600; }
|
||
|
|
|
||
|
|
.kbd {
|
||
|
|
font-family: var(--mono); font-size: 12px; background: var(--panel-2);
|
||
|
|
border: 1px solid var(--border); border-bottom-width: 3px; border-radius: 6px;
|
||
|
|
padding: 2px 7px; color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.toc { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px 24px; }
|
||
|
|
.toc a { color: var(--blue); text-decoration: none; }
|
||
|
|
.toc a:hover { text-decoration: underline; }
|
||
|
|
.toc ol { margin-left: 18px; }
|
||
|
|
.toc li { margin: 5px 0; }
|
||
|
|
|
||
|
|
.check { color: var(--green); margin-right: 6px; }
|
||
|
|
.cross { color: var(--red); margin-right: 6px; }
|
||
|
|
.arrow { color: var(--green); font-weight: 700; }
|
||
|
|
|
||
|
|
footer { margin-top: 60px; padding-top: 20px; border-top: 1px solid var(--border); color: var(--muted); font-size: 13px; text-align: center; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
|
||
|
|
<header>
|
||
|
|
<div class="wrap">
|
||
|
|
<div class="logo">📡</div>
|
||
|
|
<div>
|
||
|
|
<h1>Proximity — Testing Guide</h1>
|
||
|
|
<div class="sub">Step-by-step walkthrough for an Apple Silicon (M-series) MacBook · iOS Simulator · no physical iPhone needed for the core flow</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<div class="wrap">
|
||
|
|
|
||
|
|
<!-- ================= TOC ================= -->
|
||
|
|
<div class="toc">
|
||
|
|
<strong>Contents</strong>
|
||
|
|
<ol>
|
||
|
|
<li><a href="#overview">What you're testing & the big idea</a></li>
|
||
|
|
<li><a href="#prereqs">Prerequisites (install these first)</a></li>
|
||
|
|
<li><a href="#structure">Project structure map</a></li>
|
||
|
|
<li><a href="#server">Start the reference backend</a></li>
|
||
|
|
<li><a href="#xcode">Open & configure the project in Xcode</a></li>
|
||
|
|
<li><a href="#run">Run the app in the Simulator</a></li>
|
||
|
|
<li><a href="#walkthrough">Walk through the interaction loop</a></li>
|
||
|
|
<li><a href="#twosim">Two-simulator mutual reveal test</a></li>
|
||
|
|
<li><a href="#uitests">Run the automated UI tests</a></li>
|
||
|
|
<li><a href="#reald">Testing on a real iPhone (the sensors)</a></li>
|
||
|
|
<li><a href="#troubleshoot">Troubleshooting on M-series</a></li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 1. OVERVIEW ================= -->
|
||
|
|
<h2 id="overview"><span class="step-num">1</span> What you're testing & the big idea</h2>
|
||
|
|
<p>
|
||
|
|
Proximity is a social app where you can only connect with people you're
|
||
|
|
<strong>physically near</strong>. The make-or-break mechanic is the
|
||
|
|
<em>interaction moment</em>: silhouette → wave → mutual reveal → chat.
|
||
|
|
</p>
|
||
|
|
<div class="card success">
|
||
|
|
<strong>The key insight:</strong> All the <em>interaction logic</em> — the part that makes or breaks the
|
||
|
|
community — is fully testable in the <strong>iOS Simulator</strong>, with no iPhone. Only the physical
|
||
|
|
<em>sensors</em> (UWB, NFC) need real hardware. We built a <code>SimulatedAdapter</code> that stands in for
|
||
|
|
the hardware, so the app behaves identically.
|
||
|
|
</div>
|
||
|
|
<table>
|
||
|
|
<tr><th>Capability</th><th>Simulator</th><th>Real iPhone</th></tr>
|
||
|
|
<tr><td>UI, navigation, chat</td><td class="yes">✅</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>Auth (Apple / Google)</td><td class="yes">✅</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>Backend + WebSocket live loop</td><td class="yes">✅</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>Reveal flow (wave → mutual → chat)</td><td class="yes">✅</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>Simulated encounters / crowd</td><td class="yes">✅</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>BLE broadcast / discovery</td><td class="part">⚠️ limited</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>UWB (Nearby Interaction)</td><td class="no">❌</td><td class="yes">✅</td></tr>
|
||
|
|
<tr><td>NFC tap</td><td class="no">❌</td><td class="yes">✅</td></tr>
|
||
|
|
</table>
|
||
|
|
|
||
|
|
<!-- ================= 2. PREREQS ================= -->
|
||
|
|
<h2 id="prereqs"><span class="step-num">2</span> Prerequisites</h2>
|
||
|
|
<p>Install these before starting. On an M-series Mac, everything runs natively on Apple Silicon.</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3 style="margin-top:0">A. Xcode (required)</h3>
|
||
|
|
<p>Install from the Mac App Store, or run:</p>
|
||
|
|
<pre><code>xcode-select --install
|
||
|
|
<span class="cmt"># If you need a specific version, use Xcodes:</span>
|
||
|
|
brew install --cask xcodes</code></pre>
|
||
|
|
<p class="muted">You need Xcode 15 or later for Swift 5.9+ concurrency and SwiftData. Xcode 16 recommended.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3 style="margin-top:0">B. Node.js (required for the reference server)</h3>
|
||
|
|
<p>Check if you have it:</p>
|
||
|
|
<pre><code>node --version <span class="cmt"># want v18+</span>
|
||
|
|
npm --version</code></pre>
|
||
|
|
<p>If not, install via Homebrew (native arm64 build):</p>
|
||
|
|
<pre><code>brew install node</code></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3 style="margin-top:0">C. Homebrew (recommended)</h3>
|
||
|
|
<pre><code>/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</code></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card tip">
|
||
|
|
<strong>M-series note:</strong> Everything here is native arm64. You do <em>not</em> need Rosetta for this
|
||
|
|
project. If you ever see "running under Rosetta" in Activity Monitor for a tool, it's optional and not required.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 3. STRUCTURE ================= -->
|
||
|
|
<h2 id="structure"><span class="step-num">3</span> Project structure map</h2>
|
||
|
|
<p>Here's what you're working with:</p>
|
||
|
|
<pre><code>Nearfield_Friends/
|
||
|
|
├── Proximity/ <span class="cmt"># iOS app (SwiftUI)</span>
|
||
|
|
│ ├── App/ <span class="cmt"># entry point, AppState wiring</span>
|
||
|
|
│ ├── Models/ <span class="cmt"># Encounter, Connection, DistanceTier…</span>
|
||
|
|
│ ├── Services/
|
||
|
|
│ │ ├── Proximity/ <span class="cmt"># PresenceEngine + adapters + SimulatedAdapter</span>
|
||
|
|
│ │ ├── Network/ <span class="cmt"># APIClient, RealtimeClient (WebSocket)</span>
|
||
|
|
│ │ ├── Auth/ <span class="cmt"># Apple/Google sign-in, profile import</span>
|
||
|
|
│ │ └── Security/ <span class="cmt"># tokens, E2E crypto</span>
|
||
|
|
│ ├── ViewModels/ <span class="cmt"># Presence, Chat, Reveal</span>
|
||
|
|
│ └── Views/ <span class="cmt"># SwiftUI screens + DebugMenu</span>
|
||
|
|
├── ProximityUITests/ <span class="cmt"># automated UI tests</span>
|
||
|
|
├── Proximity.xcodeproj/ <span class="cmt"># Xcode project (generated)</span>
|
||
|
|
├── scripts/
|
||
|
|
│ └── generate_xcodeproj.py <span class="cmt"># regenerates the .xcodeproj</span>
|
||
|
|
├── server/ <span class="cmt"># reference backend (Node/TS)</span>
|
||
|
|
│ └── src/index.ts
|
||
|
|
└── docs/
|
||
|
|
├── backend-api-spec.md <span class="cmt"># API contract</span>
|
||
|
|
├── TESTING.md <span class="cmt"># testing notes</span>
|
||
|
|
└── TESTING_GUIDE.html <span class="cmt"># this file</span></code></pre>
|
||
|
|
|
||
|
|
<div class="card success">
|
||
|
|
<strong>Good news:</strong> A <code>Proximity.xcodeproj</code> is now included in the repo (generated by
|
||
|
|
<code>scripts/generate_xcodeproj.py</code>). It contains the app target, the UI test target, and a shared
|
||
|
|
scheme — so you can open it directly in Xcode and run <code>xcodebuild test</code> with no manual setup.
|
||
|
|
If you ever add/remove Swift files, re-run the generator to keep the project in sync.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 4. SERVER ================= -->
|
||
|
|
<h2 id="server"><span class="step-num">4</span> Start the reference backend</h2>
|
||
|
|
<p>The Swift app in DEBUG mode points at a local server on <code>localhost:8080</code>. Start it first.</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<p>Open <strong>Terminal</strong> and run:</p>
|
||
|
|
<pre><code>cd ~/path/to/Nearfield_Friends/server
|
||
|
|
npm install
|
||
|
|
npm run dev</code></pre>
|
||
|
|
<p>You should see:</p>
|
||
|
|
<pre><code>Proximity reference server on :8080</code></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<strong>Keep this terminal window open.</strong> 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.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card tip">
|
||
|
|
<strong>Quick sanity check:</strong> In a second terminal, run
|
||
|
|
<code>curl http://localhost:8080/v1/nearby?geohash=abc&tier=1</code> — you should get <code>[]</code> back.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 5. XCODE ================= -->
|
||
|
|
<h2 id="xcode"><span class="step-num">5</span> Open & configure the project in Xcode</h2>
|
||
|
|
|
||
|
|
<h3>5a. Open the project</h3>
|
||
|
|
<div class="card">
|
||
|
|
<p>The <code>Proximity.xcodeproj</code> is already generated. Open it:</p>
|
||
|
|
<pre><code>open Proximity.xcodeproj</code></pre>
|
||
|
|
<p>or double-click it in Finder. It includes the app target, the <code>ProximityUITests</code> target, and a
|
||
|
|
shared scheme, so everything is ready to go.</p>
|
||
|
|
<p class="muted">The entry point is <code>ProximityApp.swift</code> (marked <code>@main</code>).</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card tip">
|
||
|
|
<strong>Keeping the project in sync:</strong> If you add or remove Swift files, regenerate the project so the
|
||
|
|
file list stays correct:
|
||
|
|
<pre><code>python3 scripts/generate_xcodeproj.py</code></pre>
|
||
|
|
Then re-open the project in Xcode.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h3>5b. Set the signing team (free account is fine)</h3>
|
||
|
|
<div class="card">
|
||
|
|
<ol>
|
||
|
|
<li>Select the <strong>Proximity</strong> target in the project navigator.</li>
|
||
|
|
<li>Go to <strong>Signing & Capabilities</strong>.</li>
|
||
|
|
<li>Check <strong>"Automatically manage signing"</strong>.</li>
|
||
|
|
<li>Pick your <strong>Team</strong> (your Apple ID — free accounts work for the simulator).</li>
|
||
|
|
<li>Set a unique <strong>Bundle Identifier</strong>, e.g. <code>com.yourname.proximity</code>.</li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h3>5c. Add required capabilities (for real devices later)</h3>
|
||
|
|
<div class="card">
|
||
|
|
<p>For the simulator you only strictly need these for the real-hardware build, but add them now so the
|
||
|
|
project is ready:</p>
|
||
|
|
<ul>
|
||
|
|
<li><strong>Near Field Communication Tag Reading</strong> (Core NFC)</li>
|
||
|
|
<li><strong>Bluetooth</strong> (Core Bluetooth)</li>
|
||
|
|
<li><strong>Location</strong> (Core Location)</li>
|
||
|
|
</ul>
|
||
|
|
<p class="muted">The simulator ignores UWB/NFC but will use simulated location.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h3>5d. Add the Info.plist usage strings</h3>
|
||
|
|
<div class="card">
|
||
|
|
<p>Add these to <code>Info.plist</code> so permissions work:</p>
|
||
|
|
<pre><code>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."</code></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 6. RUN ================= -->
|
||
|
|
<h2 id="run"><span class="step-num">6</span> Run the app in the Simulator</h2>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<ol>
|
||
|
|
<li>At the top of Xcode, pick a simulator from the device dropdown
|
||
|
|
(e.g. <strong>iPhone 15</strong> or <strong>iPhone 15 Pro</strong>).</li>
|
||
|
|
<li>Press <span class="kbd">⌘R</span> to build & run.</li>
|
||
|
|
<li>The first build may take a minute. The Simulator window opens with the app.</li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card success">
|
||
|
|
<strong>What happens automatically in DEBUG builds:</strong>
|
||
|
|
<ul>
|
||
|
|
<li><span class="arrow">→</span> <code>APIClient</code> points at <code>http://localhost:8080</code></li>
|
||
|
|
<li><span class="arrow">→</span> <code>SimulatedAdapter</code> is injected into the engine (no hardware)</li>
|
||
|
|
<li><span class="arrow">→</span> A debug menu (hammer icon) appears in the toolbar</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<strong>If the app can't reach the server:</strong> the simulator shares your Mac's network, so
|
||
|
|
<code>localhost</code> works. If you see connection errors, make sure the server from
|
||
|
|
<a href="#server">Step 4</a> is still running, then use the debug menu's <em>"Reconnect socket"</em>.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 7. WALKTHROUGH ================= -->
|
||
|
|
<h2 id="walkthrough"><span class="step-num">7</span> Walk through the interaction loop</h2>
|
||
|
|
<p>This is the heart of the product. You'll simulate a full encounter with no hardware.</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3 style="margin-top:0">Step 1 — Sign in</h3>
|
||
|
|
<p>Tap <strong>Continue with Google</strong> (or Apple). The reference server accepts any token, so this
|
||
|
|
succeeds immediately. You'll land on the main screen.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 2 — Go present</h3>
|
||
|
|
<p>Tap the big circular <strong>"Tap to be Present"</strong> button. It turns green and reads
|
||
|
|
<strong>"I'm Here"</strong>. This opens the live socket.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 3 — Simulate an encounter</h3>
|
||
|
|
<ol>
|
||
|
|
<li>Tap the <strong>hammer icon</strong> (top-left) to open the Debug menu.</li>
|
||
|
|
<li>Tap <strong>"Simulate someone walking by"</strong>.</li>
|
||
|
|
<li>Go back. A <strong>silhouette</strong> ("Someone nearby") appears in the feed.</li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 4 — Wave</h3>
|
||
|
|
<p>Tap <strong>Wave</strong> on the silhouette. It changes to <strong>"Waved ✓"</strong> — you're now waiting
|
||
|
|
for the other person.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 5 — Complete the reveal (two ways)</h3>
|
||
|
|
<p><strong>Option A — two simulators</strong> (recommended, <a href="#twosim">see next section</a>): the server
|
||
|
|
relays the mutual wave and the reveal triggers automatically.</p>
|
||
|
|
<p><strong>Option B — single simulator:</strong> the reveal needs the other side to wave. Use the debug menu's
|
||
|
|
<em>"Simulate a crowd"</em> to generate more silhouettes, or drive the mutual wave programmatically via the
|
||
|
|
simulated adapter.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 6 — The reveal moment</h3>
|
||
|
|
<p>When both sides wave, <code>RevealView</code> presents full-screen: two avatars converge. Tap
|
||
|
|
<strong>"Say hello"</strong> to open the E2E-encrypted chat.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Step 7 — Chat</h3>
|
||
|
|
<p>Type a message and send. In a two-simulator setup, the other side receives it decrypted in real time over
|
||
|
|
the WebSocket.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 8. TWO SIM ================= -->
|
||
|
|
<h2 id="twosim"><span class="step-num">8</span> Two-simulator mutual reveal test</h2>
|
||
|
|
<p>This tests the real-time mutual flow end-to-end across two "people".</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<ol>
|
||
|
|
<li>Make sure the reference server is running (<a href="#server">Step 4</a>).</li>
|
||
|
|
<li>In Xcode, boot a second simulator: <span class="kbd">File</span> → <span class="kbd">Open Simulator</span>,
|
||
|
|
or use <span class="kbd">⌃⌘R</span> after adding a second scheme destination.</li>
|
||
|
|
<li>Run the app on <strong>both</strong> simulators (e.g. iPhone 15 + iPhone 15 Pro).</li>
|
||
|
|
<li>In each, sign in and go present.</li>
|
||
|
|
<li>In each, use the debug menu to simulate an encounter (each gets a distinct token).</li>
|
||
|
|
<li>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 <code>mutual</code> to <strong>both</strong> sockets.</li>
|
||
|
|
<li>The reveal triggers on <strong>both</strong> devices in real time.</li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card tip">
|
||
|
|
<strong>Tip:</strong> To run two instances easily, create a second scheme
|
||
|
|
(<span class="kbd">Product</span> → <span class="kbd">Scheme</span> → <span class="kbd">New Scheme</span>) and
|
||
|
|
set a different simulator destination, then run both schemes.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 9. UI TESTS ================= -->
|
||
|
|
<h2 id="uitests"><span class="step-num">9</span> Run the automated UI tests</h2>
|
||
|
|
<p>The <code>ProximityUITests</code> target drives the happy path automatically.</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<ol>
|
||
|
|
<li>Ensure the server is running.</li>
|
||
|
|
<li>In Xcode, select the <strong>ProximityUITests</strong> test target.</li>
|
||
|
|
<li>Press <span class="kbd">⌘U</span> to run all tests.</li>
|
||
|
|
</ol>
|
||
|
|
<p>Or from the terminal:</p>
|
||
|
|
<pre><code>cd ~/path/to/Nearfield_Friends
|
||
|
|
xcodebuild test \
|
||
|
|
-project Proximity.xcodeproj \
|
||
|
|
-scheme Proximity \
|
||
|
|
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||
|
|
-only-testing:ProximityUITests/ProximityFlowUITests</code></pre>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card success">
|
||
|
|
<strong>Ready to go:</strong> The <code>ProximityUITests</code> target and its shared scheme are already part of
|
||
|
|
<code>Proximity.xcodeproj</code>, so <span class="kbd">⌘U</span> and the <code>xcodebuild test</code> command work
|
||
|
|
out of the box.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 10. REAL DEVICE ================= -->
|
||
|
|
<h2 id="reald"><span class="step-num">10</span> Testing on a real iPhone (the sensors)</h2>
|
||
|
|
<p>When you're ready to test the actual UWB / BLE / NFC radios:</p>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<ol>
|
||
|
|
<li><strong>Disable the simulated adapter.</strong> Build a Release (non-DEBUG) configuration, or set
|
||
|
|
<code>AppState.isSimulated = false</code>.</li>
|
||
|
|
<li>Use <strong>two physical iPhones</strong> (iPhone 11+ for UWB).</li>
|
||
|
|
<li>Point the app at a reachable backend (change <code>APIClient.baseURL</code> to your Mac's LAN IP or a
|
||
|
|
deployed server).</li>
|
||
|
|
<li>Trust the developer certificate on each device (Settings → General → VPN & Device Management).</li>
|
||
|
|
<li>Run from Xcode with the device selected. The real <code>UWBAdapter</code>, <code>BLEAdapter</code>,
|
||
|
|
<code>BeaconAdapter</code> take over.</li>
|
||
|
|
</ol>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card danger">
|
||
|
|
<strong>Important:</strong> UWB and NFC are <em>not</em> available in the simulator. Only test these on real
|
||
|
|
hardware. Everything else works identically — that's the point of the abstraction.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- ================= 11. TROUBLESHOOT ================= -->
|
||
|
|
<h2 id="troubleshoot"><span class="step-num">11</span> Troubleshooting on M-series</h2>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">App won't connect to the server</h3>
|
||
|
|
<p><span class="check">✔</span> Confirm the server terminal shows <code>:8080</code> and is still running.<br>
|
||
|
|
<span class="check">✔</span> Use the debug menu's <em>"Reconnect socket"</em>.<br>
|
||
|
|
<span class="check">✔</span> Try <code>curl http://localhost:8080/v1/nearby?geohash=abc&tier=1</code> in Terminal.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">Build fails / missing files</h3>
|
||
|
|
<p><span class="check">✔</span> Make sure all folders under <code>Proximity/</code> are added to the target
|
||
|
|
(not just referenced).<br>
|
||
|
|
<span class="check">✔</span> Confirm <code>ProximityApp.swift</code> is the <code>@main</code> entry and only one
|
||
|
|
<code>@main</code> exists.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">Signing errors</h3>
|
||
|
|
<p><span class="check">✔</span> For the simulator you can often use <em>"Sign to Run Locally"</em> / no team.<br>
|
||
|
|
<span class="check">✔</span> Use a unique bundle identifier.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">"Rosetta" warnings or arch issues</h3>
|
||
|
|
<p><span class="check">✔</span> This project is native arm64 — no Rosetta needed.<br>
|
||
|
|
<span class="check">✔</span> If a tool was installed via an Intel package, reinstall via <code>brew</code>
|
||
|
|
(arm64) or use the Apple Silicon build.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">Simulator can't find localhost</h3>
|
||
|
|
<p><span class="check">✔</span> The simulator shares the Mac's network, so <code>localhost</code> resolves to
|
||
|
|
your Mac. If you moved the server to another machine, update <code>APIClient.local()</code> to that IP.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card warn">
|
||
|
|
<h3 style="margin-top:0">Mutual reveal never triggers in single-simulator mode</h3>
|
||
|
|
<p><span class="check">✔</span> A reveal needs <em>both</em> sides to wave. Use the two-simulator setup
|
||
|
|
(<a href="#twosim">Step 8</a>) for the full end-to-end test, or drive the mutual wave programmatically.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<footer>
|
||
|
|
Proximity · Testing Guide · Apple Silicon (M-series) · iOS Simulator-first workflow
|
||
|
|
</footer>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
</html>
|