- macOS 14.0+ (Sonoma or later)
- Xcode 15.0+
- XcodeGen (
brew install xcodegen) - Apple Developer account with HealthKit entitlements
- iPhone running iOS 17+ and/or Apple Watch running watchOS 10+
# Clone the repository
git clone <repo-url>
cd Apple-watch
# Generate the Xcode project
cd apps/HeartCoach
xcodegen generate
open Thump.xcodeproj| Directory | Purpose |
|---|---|
apps/HeartCoach/iOS/ |
iPhone app (Views, ViewModels, Services) |
apps/HeartCoach/Watch/ |
Apple Watch app |
apps/HeartCoach/Shared/ |
Code shared between iOS and watchOS targets |
apps/HeartCoach/Tests/ |
Unit tests |
apps/HeartCoach/web/ |
Landing page and legal documents |
scripts/ |
Research and data pipeline scripts |
data/ |
Market research data |
- All
ObservableObjectclasses must be@MainActor WCSessionDelegatemethods must benonisolatedwithTask { @MainActor in }hops- Use
Task { @MainActor [weak self] in }for cross-isolation property mutations - Never use
DispatchQueue.main.asyncfor@Publishedproperty updates in@MainActorclasses
- All
HKQuantityType/HKCategoryTypeconstructors must useguard let(never force-unwrap) - Apple hides read authorization status — do not check
authorizationStatusfor read permissions - Validate
days > 0before constructing date ranges
- Health data must be encrypted before persisting (use
CryptoService) - Keychain operations must be serialized (see
CryptoService.keyLock) - Never overwrite existing Keychain entries on
errSecDuplicateItem— re-read first - Never store health data in URL parameters or analytics events
- Use suggestive language ("consider", "try", "you might") not prescriptive ("you should", "you must")
- Never claim to diagnose, treat, or prevent any condition
- Maintain FDA general wellness exemption positioning
- Include health disclaimer in onboarding flow
# Run unit tests
xcodebuild test -scheme Thump -destination 'platform=iOS Simulator,name=iPhone 15'main— production-ready codefeature/*— new featuresfix/*— bug fixesrelease/*— release preparation
Write concise commit messages that describe what changed and why:
Fix PaywallView infinite alert loop from .constant() binding
.constant() creates a read-only binding that ignores dismissal writes,
causing the alert to reappear immediately. Replace with custom Binding
that clears purchaseError on dismiss.