initial commit: Proximity iOS app proposal, full Swift code scaffold, reference backend, testing docs, generated Xcode project

This commit is contained in:
2026-08-12 00:22:01 +00:00
commit 9d1b0f8dd9
52 changed files with 6271 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
import Foundation
import Combine
/// View model for the presence experience.
///
/// Exposes the state the UI needs to render the "am I present?" moment, the
/// radius tier picker, and the live feed of encounters (silhouettes).
@MainActor
final class PresenceViewModel: ObservableObject {
@Published var isPresent: Bool = false
@Published var radiusTier: DistanceTier = .rightHere
@Published private(set) var activeEngines: Set<ProximityEngine> = []
@Published private(set) var encounters: [Encounter] = []
@Published private(set) var lastScan: Date?
private let engine: PresenceEngine
private var cancellables = Set<AnyCancellable>()
init(engine: PresenceEngine) {
self.engine = engine
engine.$isPresent
.assign(to: &$isPresent)
engine.$radiusTier
.assign(to: &$radiusTier)
engine.$activeEngines
.assign(to: &$activeEngines)
engine.$recentEncounters
.assign(to: &$encounters)
engine.$lastScan
.assign(to: &$lastScan)
}
// MARK: - User actions
func togglePresence() {
isPresent.toggle()
Task { await engine.setPresent(isPresent) }
}
func setRadius(_ tier: DistanceTier) {
radiusTier = tier
Task { await engine.setRadiusTier(tier) }
}
/// Send a "wave" to a silhouette the first step toward mutual reveal.
func wave(to encounter: Encounter) {
// In production: POST /v1/wave with the encounter token.
Task {
await engine.wave(to: encounter)
}
}
func block(_ encounter: Encounter) {
Task {
await engine.block(encounter)
}
}
}