Imported from finbar-tracey/metricly (
AGENTS.md). Install upstream withnpx skills add finbar-tracey/metricly. Copyright stays with the author.
Metricly — agent guide
Targets
| Target | Path | Role |
|---|---|---|
| tracker | tracker/ |
iPhone/iPad app |
| MetriclyWatch | MetriclyWatch/ |
watchOS app |
| MetriclyWidgetsExtension | MetriclyWidgets/ |
Widgets + Live Activity |
| trackerTests | trackerTests/ |
XCTest (engines, migrations, import) |
| trackerUITests | trackerUITests/ |
UI smoke tests (optional in test plan) |
Build & test
open tracker.xcodeproj
xcodebuild test -scheme tracker \
-destination 'platform=iOS Simulator,name=iPhone 17' \
-testPlan tracker
Shared schemes: tracker, MetriclyWidgetsExtension.
Adding files under Services/ or Helpers/
Unlike tracker/ (filesystem-synced in Xcode), new Swift files under Services/ and Helpers/ must be added to tracker.xcodeproj/project.pbxproj manually:
- Create the file on disk (e.g.
Services/Strava/StravaAPIClient.swift). - Add a
PBXFileReferenceandPBXBuildFile, group under the correct folder (Strava,HealthKit,Helpers). - Include the build file in the tracker target
PBXSourcesBuildPhase(not only Watch/Widgets unless intended). - Build tracker — missing membership shows up as “Cannot find type … in scope” in unrelated views.
Watch (MetriclyWatch/): same manual step for new section files (e.g. WatchGym*Sections.swift).
Architecture
- SwiftData schema:
Services/MetriclySchema.swift - Engines (pure):
tracker/Models/Engines/ - Stores:
tracker/Models/Stores/ - Services (IO):
Services/— HealthKit, Strava, connectivity, widgets - Sync:
MetriclySyncCoordinator,AppLifecycleCoordinator,WatchContextBuilder,WatchPayloadPersistence,PhoneConnectivityManager - Appearance:
UserSettings.appearanceModeonly (AppearanceModehelper) - Routing:
AppRouterviaappServices.routerinContentViewand feature views;AppRouter.sharedonly intrackerAppbootstrap - DI:
AppServices(router,phoneConnectivity,strava,workoutActivity,healthKit,healthDataCache,appErrorBus,syncStatus,cardioTracker,openURL/openSettings) via@Environment(\.appServices); exceptions:UIApplicationfor keyboard/resign, window scene in cardio share,AppServices.sharedin app bootstrap andHomeSyncStatusPill.shouldShow,StravaSettingsSectionpreview default. - AE–AH (last refactor wave): section round 3 (
StreakCalendar*,CardioGoals*,HeartRateDetail*splits); coordinatorsFullWorkoutListView,TrainingProgramsView,SmartSuggestionsView+SmartSuggestionsEngine,WorkoutDetailScreenSections - Post-refactor (maintenance): split section/service files only when editing a feature and file is > ~350 LOC; prefer product work and targeted a11y on hubs (Training, Home hero, finish workout)
- App shell:
ContentView+ContentView+*.swiftextensions (tabs, iPad/iPhone layout, lifecycle) - Timers:
RestTimerController(rest),WorkoutDurationTracker(elapsed workout),WorkoutIntervalTimerController(EMOM/AMRAP/Tabata) - Home:
HomeDashboardQueryContainerholds all@Query;HomeDashboardScreenis query-free - Health details:
MetricDetailScaffoldon Steps, HR, Water (chart), Sleep, Caffeine (history) - Reports:
WeeklyMonthlyReportEngine+ splitWeeklyMonthlyReport*Sections; slimWeeklyMonthlyReportView - Time ranges:
DetailTimeRange(7D/30D/90D) on health details;VolumeTrendPeriod(weekly/monthly) onVolumeTrendsView - Workout detail:
WorkoutDetailQueryContainer+ slimWorkoutDetailScreen; sections undertracker/Views/Workout/ - Finish workout:
FinishWorkoutSummarySection+FinishWorkoutFeedbackSection; slimFinishWorkoutSheet - Caffeine / Creatine / Body weight:
CaffeineEngine,CreatineEngine,BodyWeightEngine+CaffeineLoggingSections/CaffeineHistorySections(facadeCaffeineTrackerSections) undertracker/Views/Nutrition/; slim coordinators - Sleep / Steps / Water detail:
SleepEngine(optional),SleepDetailSections,StepsDetailSections,WaterTrackerSectionsundertracker/Views/Health/ - Activity / Workout compare:
ActivityLog*Section,WorkoutComparisonSectionsundertracker/Views/Activity/andWorkout/ - Cardio active:
CardioActiveMapSection+CardioActiveMetricsSection+CardioActiveControlsSection; slimCardioActiveView - Cardio / training hubs:
CardioHubSections,CardioGoalsSections,TrainingProgramsSections,OneRepMaxSections+OneRepMaxEngine - Exercise / progress:
ExerciseHistorySections,ProgressPhotosSections - Cardio session detail:
CardioSessionDetailSections+CardioSessionMapSection+CardioSessionActionsSection - Achievements / Cardio bests:
AchievementsEngine+ section views;CardioBestsSummarySection+CardioBestsRecordsSection - Heart rate detail:
HeartRateDetailSections+ slimHeartRateDetailView - Muscle recovery:
MuscleRecoverySections+ slimMuscleRecoveryView - Body fat / Streak calendar:
BodyFatEstimateSections,StreakCalendarSections+ slim coordinators - Health hub / Patterns:
HealthDashboardSections,PersonalInsightsSections+ slim coordinators - Onboarding: per-page views under
tracker/Views/Onboarding/; coordinator inOnboardingView - Settings: section views under
tracker/Views/Settings/ - Watch:
WatchContextBuilderinServices/WatchContextBuilder.swift - Widgets: views call
MetriclySyncCoordinatoronly (notWidgetDataWriterdirectly) - Section modules: large detail UIs split into hero/chart/card files with thin forwarding enums (e.g.
CaffeineLoggingHeroSections,SleepDetailChartTrendSections); second-pass trims keep implementation files ≤ ~350 LOC - Workout utilities (AA):
TrainingHubSections,VolumeTrendsSections+VolumeTrendsEngine,HomeDashboardScreen+Cards/+Lifecycle,TrainingBlockDetailSections,WorkoutTimerSections,BodyMeasurementsSections+BodyMeasurementsEngine,PlateCalculatorSections+PlateCalculatorEngine,WorkoutCalendarSections— coordinators under ~200 LOC - HealthKit / connectivity (AC):
Services/HealthKit/(HealthKitMetricsFetcher,HealthKitWorkoutWriter,HealthKitCardioWriter,HealthKitSleepModels) + slimHealthKitManagerfacade;PhoneConnectivityMessageHandler,PhoneConnectivityManager+Session+ slimPhoneConnectivityManager - Strava (AH):
Services/Strava/(StravaAPIClient,StravaAuth,StravaTokenRefresher) +StravaService.swiftfacade (upload types +uploadActivity/fetchActivitieslive here for target visibility) - Import (AH):
ImportHelper(plan + CSV utilities),ImportCommit(SwiftData insert),ImportParsers(Strong/Hevy),ImportFormats(IR + detect) - Watch gym (AH):
WatchGymSections,WatchGymControlsSections,WatchGymSetListSections+ slimWatchGymView - MetriclyCore package: still deferred (post AE–AH re-audit) — gate unchanged:
WatchSyncModels+WidgetModelsare multi-target shared DTOs;WatchModelsdocuments watch-only App Group keys (no fourth duplicated formatter/DTO body). Revisit when a fourth shared copy appears. - CI: PRs —
trackerTests(xcodebuild-test.yml), SwiftLint strict on macOS CI (swiftlint.yml, config.swiftlint.yml); nightly full plan (xcodebuild-test-nightly.yml) - UI smoke:
WorkoutFlow,CaffeineFlow,TrainingHubFlow,VolumeTrendsFlow,WorkoutCalendarFlow, onboarding, smoke - Extensions:
WatchSyncModels,WidgetModels(Foundation-only, multi-target)
Product rules
- Corrupted store → quarantine, not delete (
trackerApp) - Apply Adjustments never edits logged sets
- Watch context:
PhoneConnectivityManager.collectWatchContext()/pushWatchContext()
Strava (optional)
Config/Secrets.xcconfig with STRAVA_CLIENT_ID / STRAVA_CLIENT_SECRET (gitignored).