Concepts

Architecture

TripLibre is two things: a thin SwiftUI app that talks to iOS, and a platform-independent Swift package, HHTCore, that does everything else. All logic that matters for data quality lives in the package and is tested without a phone.

Data flow

The collector writes every fix to SQLite immediately. Inference runs on launch, when the app comes to the foreground, when the collector decides you've stopped, and from a background refresh task. It is deterministic: the same raw data and the same corrections always produce the same diary. After each run (and after any edit) the app also refreshes a small summary for the Home Screen widgets.

Repository layout

App/                     SwiftUI iOS app
  HHTApp.swift           entry point, AppDelegate, background refresh task
  AppModel.swift         owns the store and services; views observe it
  Location/              LocationCollector, MotionSync, PlaceGeocoder
  Screens/               Today, Timeline, Map, Stats, Places, Settings, detail screens
  Support/               theme, formatting, demo data, Info.plist, WidgetBridge
Widgets/                 WidgetKit extension: read-only Home Screen widgets
Packages/HHTCore/        Swift package, no third-party dependencies
  Sources/HHTCore/
    Database/            SQLite wrapper, schema + migrations, TravelStore
    Inference/           StayDetector, ModeClassifier, InferenceEngine, InferenceConfig
    Editing/             EditService (every correction) + audit log
    Analytics/           MobilityMetrics (HHTS metrics, r_g, entropy, tours…)
    Export/              Exporter, Importer, CSV
    Geo/                 haversine, centroids, convex hull, Douglas–Peucker, polyline
    Models/              records (Place, Visit, Trip…) and enums (TravelMode…)
    Widget/              WidgetSnapshot, the summary the widgets read
  Tests/HHTCoreTests/    fixture scenarios, edits, reprocessing, export
analysis/                Python loader (hht.py), example analysis, export contract check
docs/                    hhtdiary.org: organization pages (/, vision/, roadmap/, products/), these docs, markdown specs
project.yml              XcodeGen spec — HHT.xcodeproj is generated from it
Makefile                 test / sim / phone / ci workflows

The two halves

App (iOS-only)

Everything that needs iOS frameworks: LocationCollector (a two-state machine, moving vs stationary, described below), MotionSync (queries CoreMotion's ~7-day activity history when inference runs), PlaceGeocoder (optional reverse geocoding), and the SwiftUI screens. AppModel is a single shared object that owns the TravelStore, InferenceEngine and EditService. Views observe its revision counter and reload after any change.

HHTCore (pure Swift)

No UIKit, no CoreLocation, no dependencies beyond Foundation and the system SQLite. It builds and tests with plain swift test on macOS, which is what CI runs. The main types:

TypeResponsibility
TravelStoreTyped access to the SQLite database: points, motion, places, visits, trips, flights, phases, audit log.
InferenceEngineIncremental and range reprocessing. Orchestrates the detectors and protects user edits.
StayDetectorCleans fixes and finds stays (visits) with sequential clustering and gap reasoning.
ModeClassifierClassifies intervals, segments trips into modes, picks the main mode.
EditServiceEvery user correction, each one transactional and audit-logged.
MobilityMetricsSurvey-style metrics for any date range.
Exporter / ImporterThe export bundle; CSV, GPX and airport-list import.
WidgetSnapshotThe small, versioned summary the Home Screen widgets read (counts, labels, times).

Three data layers

The single most important design rule: raw observations, automatic guesses and your corrections are kept apart. That's what makes it safe to re-run inference at any time.

1
Raw observationsraw_location_points, motion_activities. Written by the collector, append-only, never edited.
2
Inferred eventsvisits, trips, trip_segments with user_status = 'auto'. Written by inference; may be rebuilt at any time.
3
Canonical, user-correctedSame tables with user_status in confirmed | edited | manual. Locked against rebuilds; every change in audit_log.

Background collection

The collector switches between two states to balance accuracy and battery:

StateLocation settingsLeaves when
movingContinuous updates. Balanced: 10 m accuracy / 15 m filter. Precise: best / 5 m.No fix > 60 m from an anchor for 3 min → stationary
stationaryStandard updates off. 150 m geofence around the stop; CLVisit and significant-change monitoring on.Geofence exit, CLVisit departure, or a significant-change fix > 150 m away → moving

Home Screen widgets

The HHTWidgets extension (Widgets/) shows the diary on the Home Screen. It is a window onto the app, not a second engine: it never opens the database, collects location or runs inference.

StepWhereWhat happens
BuildWidgetSnapshot.build(store:…) (HHTCore)Today counts with the same overlap rules as the Today screen; 7-day totals from the same Analytics.compute as Stats; tracking state, last point, trips to check.
WriteWidgetBridge (App/Support)Runs 2 s after a revision bump, a location-permission change or a collector mode change, at most every 10 min while new fixes arrive, and when the app goes to the background. Writes widget-snapshot.json atomically into the App Group group.com.zyang91.hht.
ReloadWidgetCenterAsked to reload only when the content changed (or at most hourly), to stay inside iOS's reload budget.
ShowSnapshotProvider (Widgets)Reads the file once per timeline and adds an entry every 15 min plus one at midnight, so "3 min ago" and the day rollover stay right without the app running. After midnight the week widget relabels itself "7 days to <day>" until a new snapshot arrives.

What each widget looks like is on the UI guide.

Design priorities

When two goals conflict, the earlier one wins:

  1. Data integrity: never lose or silently change what was recorded or corrected.
  2. Collection reliability: keep recording in the background.
  3. Correct reconstruction.
  4. Fast correction: one or two taps.
  5. Exportability.
  6. Analytics.
  7. Polish.

Build and CI

There is no Docker image: iOS apps can only be built and signed on macOS with Xcode. The reproducible "package" is project.yml (XcodeGen) + Makefile + GitHub Actions on macOS runners. CI runs the core tests, an end-to-end check that the Python loader reads a real export, simulator and device builds, and a check that HHT.xcodeproj matches project.yml. See Contributing.