iOS companion app for the SardinesTrack Flask server. Syncs HealthKit data to the server, fetches flare-risk forecasts, and embeds the Log and Risk pages via WKWebView.
- TabView with Sync, Log, and Risk tabs
- WKWebView for the biotracker Log and Risk mobile pages
- HealthKit sync, manual and automatic
- Daily background sync via
BGTaskScheduler - Local notifications: bedtime, med doses, flare alerts
- Flare-status API client (
/api/flare-status) - iOS Shortcuts actions
- Historical backfill
- Settings UI
The server must expose /api/flare-status, returning JSON like:
{
"ok": true,
"date": "2026-04-05",
"score": 7.2,
"predicted_flare": false,
"score_delta": 2.1,
"factors": [...],
"doses_due": [...]
}Auth uses the same Bearer token as /api/health-sync. Smoke test:
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://sardines.duckdns.org/api/flare-status?user_id=1"Full checklist lives in XCODE_SETUP.md. Minimum config:
Info.plist keys (Target → Info)
| Key | Value |
|---|---|
BGTaskSchedulerPermittedIdentifiers |
array containing com.biotracking.healthsync.daily |
NSHealthShareUsageDescription |
description of why HealthKit access is needed |
Capabilities (Target → Signing & Capabilities)
- HealthKit, with Background Delivery on
- Background Modes, "Background processing" only
Entitlements (healthsync.entitlements)
<key>com.apple.developer.healthkit</key>
<true/>
<key>com.apple.developer.healthkit.background-delivery</key>
<true/>- Build to a real iPhone. HealthKit does not work in the Simulator.
- Sync tab → Authorize HealthKit → grant permissions.
- Fill in the settings:
- Server URL:
https://sardines.duckdns.org/api/health-sync - API Token: from Flask config
- User ID:
1
- Server URL:
- Tap Sync Health Data. Expect a status line like
synced 9 fields: steps, hrv, .... - Log tab loads the biotracker log page. Risk tab loads the forecast page.
For the full test matrix (notifications, background sync, Shortcuts), see TESTING_GUIDE.md.
- Must be a real iPhone, not the Simulator
- Health app → Sharing → Apps → healthsync should list permissions
- Info.plist must include
NSHealthShareUsageDescription
- Check Tailscale is connected on the iPhone
- Open
https://sardines.duckdns.orgin Safari to confirm reachability - Confirm the Flask server is running
- Same Tailscale check as above
- Try disconnecting and reconnecting Tailscale
- Settings → Notifications → healthsync → Allow Notifications must be on
- If the first-launch prompt was dismissed, re-enable via the in-app toggle
BGTaskScheduleris unreliable from Xcode debug builds; behavior is more consistent in TestFlight or production- Settings → General → Background App Refresh → healthsync must be on
TESTING_GUIDE.mddocuments the Xcode debug command to force a run
Swift sources:
| File | Purpose |
|---|---|
healthsyncApp.swift |
App entry point, registers background tasks |
ContentView.swift |
Main TabView |
SyncSettingsView.swift |
Settings and manual sync UI |
BioTrackerWebView.swift |
WKWebView wrapper for mobile pages |
HealthSyncer.swift |
HealthKit queries and network sync |
BackgroundSyncTask.swift |
Daily sync via BGTaskScheduler |
FlareChecker.swift |
Polls /api/flare-status, evaluates alerts |
NotificationManager.swift |
Schedules notifications |
ShortcutIntents.swift |
iOS Shortcuts actions |
Docs:
| File | Purpose |
|---|---|
ARCHITECTURE.md |
System design and data flows |
XCODE_SETUP.md |
Xcode configuration checklist |
TESTING_GUIDE.md |
Test procedures |
NOTIFICATIONS.md |
Notification design and timing |
STATUS.md |
Implementation status |
Info.plist.reference |
Example Info.plist keys |