ipad
ipad.elonn is the iPadOS AR runtime for Elonn. Bundle identifier: com.elonn.ipad. Production services are on the elonn.com domain.
The iPad runtime is a SwiftUI application that authenticates through World, fetches the world contract from world.elonn.com/world/session, and renders the two-layer model using the live device camera for the field and SwiftUI views for carry. It targets iPadOS 26 and is built with Xcode 26 (Swift 6.2). Like every Elonn runtime, it owns no state — it consumes the world contract and routes all writes back through world.
The Runtime Contract is the normative runtime contract. This page describes the current iPad implementation and platform constraints.
Presence identifier
ipad.elonn — the intended string for a member's presence record while active in the iPad runtime. As with Android, a dedicated presence write is not yet wired in the codebase.
What it renders
Field layer — Rendered over the live rear-camera feed (AVCaptureVideoPreviewLayer). Field objects from the world contract are projected to screen X by their absolute compass bearing relative to the device heading (CLLocationManager true heading), with a scan-grid and vignette overlay matching the web aesthetic. The field is a heading-driven AR overlay, not a pannable 2D panorama.
Carry layer — Rendered as SwiftUI views composited in a ZStack:
- Upper dock — reserved blank placeholders for future global signals
- Left rail — runtime/user context stack (profile, account, settings, status, diagnostics, help)
- Center stack — the focused
carry_primarysurface with neighbors partially visible; in front of the rails - Right rail — service navigation for the focused surface, or the Field control stack (Places, etc.) when Field is active
- Lower dock — one button per carry surface plus a Field toggle
User experience model
The center window sits in front of both rails. Each rail has a hamburger (≡) header that collapses the rail to its title strip without covering the center window. Each carry surface bar carries a close (✕) button; the lower-dock buttons re-open and focus a surface. Selecting Field from the lower dock hides the center window, fills the screen with the camera field, and expands the right rail to the field controls.
World contract
The runtime calls GET https://world.elonn.com/world/identity/me, then fetches GET https://world.elonn.com/world/session with a Bearer token. The contract is decoded into:
surface_runtime.surfaces/.stacks/.commands— surfaces, the three canonical stacks, and surface-write command URLsfield_runtime.objects— Maps-authored geographic objects rendered in the field layer (the runtime does not call maps directly)services.identity.members— member directoryservices.social.objects— conversations, communities, events, presenceservices.messages.objects— open one-to-one member message threads
Surface service_binding, content_source, and runtime_state are decoded permissively: PHP encodes empty associative arrays as [], so runtime_state accepts both an object and an empty array.
Field layer
Field objects come from field_runtime.objects[] in the contract — there is no separate geo fetch. Each object carries an absolute bearing_degrees and distance_meters. Projection to screen:
relativeBearing = (bearing − deviceHeading + 360) mod 360, folded to [−180, 180]
cull if |relativeBearing| > hFOV / 2 (hFOV ≈ 120°)
projectedX = (relativeBearing / hFOV + 0.5) × viewportWidth
projectedY = clamp(120 + distance × 0.28, 84, 520)
The Places control card should filter real nearby-place objects by normalized category, not by placeholder object_key prefixes.
Surface stack
A single SwiftUI stack view drives all three stacks (center carry, left rail, right rail), parameterized by kind. Gesture model mirrors the web runtime:
- Drag threshold: 0.18 × viewport height
- Velocity threshold: ±650 pt/s
- Tap (drag < 4% height, no velocity): focus that surface directly
- Carry transform:
translateY(offset × 82),scale(1 − |offset| × 0.1), opacity floor 0.34 - Rail transform:
translateY(offset × 48),scale(1 − |offset| × 0.06), opacity floor 0.42
Panel loading
Surfaces whose content_source.kind is runtime_panel render their panel inline in the carry card (Social, Messages, Calendar, Find) rather than in a floating modal. Panel data is fetched from world-relayed URLs with the Bearer token:
/world/panels/social/world/panels/messagesand/world/messages/{id}/messages/world/panels/time/world/panels/find?q=…
Relative panel paths are resolved against world.elonn.com. The runtime never constructs service URLs for hosts other than world.
Surface focus writes
surface_runtime.commands.focus_surface is captured on load. Focusing a carry surface fires a fire-and-forget POST to that World command URL with {surface_id}. World relays the write to Surface with trusted member context.
Session startup
RuntimeStore.load()—GET world.elonn.com/world/identity/mewithAuthorization: Bearer <token>- 401 → present the login
WKWebViewatelonn.com/account/login; on success theelonn_api_tokencookie value is extracted and stored as the bearer token - other error → unavailable state with retry
- 401 → present the login
GET world.elonn.com/world/session— decode and apply the contract- Render the three canonical stacks, docks, and rails
- Start compass heading updates (rotates the field); request camera permission for the field background
Auth model
The login WKWebView completes the standard browser login, after which the runtime reads the elonn_api_token cookie value and sends it as Authorization: Bearer <token> on requests to World. This avoids cross-domain cookie-sharing pitfalls on iPadOS while preserving the World-only runtime boundary.
Notable constraints
- iPad-only; landscape is primary, portrait collapses rails to header strips
- Camera feed orientation is synced to device rotation via the capture connection's
videoRotationAngle - No push notifications, no background sync
- Production
.comhosts only; no.localswitching
Notes vs. the web runtime
- Field data is read from the contract (
field_runtime.objects[]), not ageo/nearbyfetch — that path is not part of the runtime contract on production. - Panels render inline in the carry surface rather than in a single floating panel element.
- Projection is heading-driven over a live camera, not pan-driven over a flat panorama.