Skip to content

Latest commit

 

History

History
122 lines (90 loc) · 5.43 KB

File metadata and controls

122 lines (90 loc) · 5.43 KB

Architecture

HushMap is an iOS app (SwiftUI + SwiftData) with a thin, secure serverless layer for AI. This document explains how the pieces fit together and the decisions behind them.

Guiding principles

  1. Sensory data is the product. Every architectural choice serves getting an accurate noise/crowd/lighting read to the user quickly and honestly.
  2. Never ship secrets. The OpenAI key lives only in server-side Secret Manager; the app authenticates to the proxy with App Check.
  3. Degrade gracefully. AI is an enhancement, not a dependency — the map, reports, and a rule-based prediction fallback all work if AI is unavailable.
  4. Respect the device. Rendering and clustering adapt to device capability so older iPhones stay smooth.

System overview

flowchart TD
    subgraph device["iOS App - SwiftUI + SwiftData"]
        V["Views (SwiftUI)"] --> VM["ViewModels"]
        VM --> S["Service layer<br/>(MainActor singletons)"]
        S --> DB[("SwiftData")]
        S --> AC["App Check"]
    end

    subgraph cloud["Firebase"]
        CF["Cloud Functions<br/>predictSensory / interestingFact"]
        FS[("Firestore")]
        SM["Secret Manager"]
    end

    OA["OpenAI gpt-4.1-mini"]
    GM["Google Maps SDK"]
    GP["Google Places API"]

    S -->|App Check token| CF
    CF --> SM
    CF --> OA
    S <--> FS
    S --> GM
    S --> GP
Loading

Layers

Presentation (HushMap/Views, HushMap/ViewModels)

  • SwiftUI views using @State / @StateObject for owned state and @ObservedObject for injected view models.
  • View models expose @Published properties for reactive updates.
  • SwiftData @Query drives automatic UI refresh from the local store.
  • Navigation is sheet-based (no UIKit navigation controllers); onboarding/welcome flow is managed at app level in HushMapApp.

Services (HushMap/Services)

The heart of the app: 20+ @MainActor singletons, each owning one concern. Highlights:

Service Responsibility
PredictionService Orchestrates AI + algorithmic sensory predictions; blends real reports, time, weather.
OpenAIService Thin client for the Cloud Functions AI proxy (no OpenAI key on device).
SensoryProfileService On-device learning of the user's personal sensory tolerances.
SmartNotificationService Proactive warnings near incompatible venues.
AudioAnalysisService Real-time dB measurement mapped to a sensory scale.
GoogleMapsService / PlaceService Map configuration, POI/place lookups.
ReportSyncService / FirestoreService Community report sync with Firestore.
AuthenticationService Google / Apple / anonymous auth.
DeviceCapabilityService Categorizes device tier to scale rendering.

Data

  • SwiftData — local persistence via @Model types (Report, User, SensoryProfile, QuickUpdate, …). Relationships wired with proper inverses.
  • Firestore — shared community reports, synced in the background.

Serverless (functions/)

TypeScript Cloud Functions (Node 20) that proxy OpenAI:

  • predictSensory — Chat Completions with a strict JSON schema (Structured Outputs). Returns noise_level / crowd_level / lighting_level / summary.
  • interestingFact — Responses API with the hosted web_search tool for grounded facts, plus citation cleanup and a NO_FACT sentinel.

Both enforce App Check (enforceAppCheck: true) and read the OpenAI key from Secret Manager. See functions/DEPLOY.md.

Key flows

Sensory prediction

sequenceDiagram
    participant U as User
    participant App as iOS App
    participant CF as predictSensory (Cloud Function)
    participant AI as OpenAI

    U->>App: Tap a place on the map
    App->>App: Gather venue, time, weather, nearby reports
    App->>CF: call(payload) + App Check token
    CF->>AI: Chat Completions (strict JSON schema)
    AI-->>CF: { noise, crowd, lighting, summary }
    CF-->>App: Structured result
    App->>U: Render sensory levels + advice
    Note over App: On any failure → local rule-based fallback
Loading

Personal sensory profile (on-device learning)

SensoryProfileService updates the user's tolerance model from each report using comfort-weighted exponential moving averages — it learns preferences from comfortable experiences rather than inverse-correlating, which prevents preference collapse. Confidence grows logarithmically with report count and feeds both recommendations and smart notifications.

Security model

  • No embedded secrets — OpenAI key is server-side only.
  • App Check (App Attest) attests that requests come from a genuine build before the functions run.
  • Google keys are kept out of version control in Config-Local.xcconfig (gitignored) and should be bundle-ID-restricted in Cloud Console.
  • Privacy manifest (PrivacyInfo.xcprivacy) declares data collection and required-reason API use.

Performance

  • DeviceCapabilityService categorizes devices (High/Medium/Low) and scales marker complexity, clustering thresholds, and animation durations.
  • Map markers are capped and clustered on lower-end devices.
  • AI predictions are cached to reduce API calls.

Further reading