MoreSleep optimizes your device usage throughout the day and week to fix your sleep schedule and facilitate that process. Set a bedtime goal, and your phone gradually winds down with you — science-backed notifications, progressive app restrictions, Apple Watch haptics, and real health tracking.
- Architecture
- Data Flow
- Data Structures
- Deliverables & Implementation Steps
- Technical Implementation Details
- Demo Flow (5 Minutes)
- Success Criteria
- Future Enhancements (AI-Powered)
Why MVVM: It's the native SwiftUI paradigm. ViewModels are @Observable classes, Views are declarative, and services are injected via environment. Clean separation, easy to test, scales well from hackathon to production.
| Layer | Role | Technology |
|---|---|---|
| Views | Pure UI, no business logic. Bind to ViewModels. | SwiftUI (iOS + watchOS) |
| ViewModels | Transform data for display, handle user actions, call services. | @Observable classes |
| Services | Encapsulate system APIs (Notifications, WatchConnectivity, Focus). Each behind a protocol for testability. | Protocol-based, dependency-injected |
| Models | Plain Swift structs/enums for domain data. Shared between iOS and watchOS targets. | Swift structs, SwiftData @Model |
| Persistence | Local-first storage. No backend needed. | SwiftData (iOS 17+) |
| Coordinator | Manages navigation flow (Onboarding → Main Tab → Settings). | @Observable class with NavigationPath |
MoreSleep/
├── Shared/ ← Code shared between iOS and watchOS
│ ├── Models/
│ │ ├── SleepGoal.swift
│ │ ├── NightPlan.swift
│ │ ├── NotificationTemplate.swift
│ │ ├── SleepRecord.swift
│ │ ├── AppTier.swift
│ │ ├── ScienceReference.swift
│ │ └── MindfulnessExitState.swift
│ ├── Services/
│ │ └── SleepCalculator.swift ← Bedtime math, sleep debt, rewind schedule
│ └── Resources/
│ └── ScienceData.json ← All study references + notification copy
├── MoreSleep iOS/
│ ├── App/
│ │ ├── MoreSleepApp.swift ← Entry point, permission requests, DI setup
│ │ └── AppCoordinator.swift ← Navigation state machine
│ ├── ViewModels/
│ │ ├── OnboardingViewModel.swift
│ │ ├── TonightViewModel.swift
│ │ ├── ProgressViewModel.swift
│ │ └── SettingsViewModel.swift
│ ├── Views/
│ │ ├── Onboarding/
│ │ │ ├── WelcomeView.swift
│ │ │ ├── BedtimeGoalView.swift
│ │ │ ├── SetupGuideView.swift ← Night Shift + Sleep Focus deep links
│ │ │ └── PermissionsView.swift
│ │ ├── Tonight/
│ │ │ ├── TonightView.swift ← Countdown + checklist + mindfulness exit
│ │ │ ├── CountdownRing.swift ← Animated circular countdown
│ │ │ ├── ChecklistRow.swift
│ │ │ ├── ScienceModalView.swift ← "Why?" popup with study citations
│ │ │ └── MindfulnessExitView.swift← Escalating wait to opt out
│ │ ├── MockBlocking/
│ │ │ ├── MockBlockedAppsView.swift← Simulated app grid with shields
│ │ │ ├── AppShieldOverlay.swift ← "This app is sleeping" shield
│ │ │ └── MockAppTierView.swift ← Drag apps into restriction tiers
│ │ ├── Progress/
│ │ │ ├── WeekView.swift
│ │ │ └── SleepDebtCard.swift
│ │ └── Settings/
│ │ └── SettingsView.swift
│ ├── Services/
│ │ ├── NotificationService.swift ← Schedule/cancel UNNotifications
│ │ ├── WatchSyncService.swift ← WCSession management (iOS side)
│ │ ├── HealthKitService.swift ← Read sleep, HR, HRV, SpO2, temp (Phase 2)
│ │ └── FocusService.swift ← Sleep Focus scheduling (Phase 2+)
│ └── Extensions/
│ ├── Date+Sleep.swift
│ └── Color+Theme.swift
├── MoreSleep Watch/
│ ├── App/
│ │ └── MoreSleepWatchApp.swift ← Watch entry point
│ ├── Views/
│ │ ├── WatchTonightView.swift ← Bedtime countdown + simplified checklist
│ │ ├── WatchCountdownRing.swift ← Compact countdown ring for watch
│ │ └── WatchWindDownView.swift ← "Put your phone down" haptic screen
│ ├── Services/
│ │ ├── WatchSyncService.swift ← WCSession management (watch side)
│ │ └── HapticEngine.swift ← Haptic type selection + scheduling
│ └── Complications/
│ └── SleepComplications.swift ← Watch face complications
└── Assets/
└── Assets.xcassets
Core principle: Unidirectional — View → ViewModel → Service → System API → callback → ViewModel → View
- User sets current bedtime (e.g., 12:00 AM) and target bedtime (e.g., 10:30 PM)
- User sets duration (e.g., 7 days)
OnboardingViewModelcreates aSleepGoaland persists via SwiftDataSleepCalculatorgenerates 7NightPlanobjects (one per night, each 15 min earlier)NotificationServiceschedules all notifications for tonight's planWatchSyncServicesends tonight'sNightPlanto Apple Watch- App navigates to TonightView
- App foregrounds →
TonightViewModelloads today'sNightPlan SleepCalculatorcomputes time remaining, active checklist items- At each notification offset:
- Phone: system fires notification → user sees science-backed reminder
- Watch:
HapticEnginefires haptic matching current time-to-bed intensity
- 15 min before block: phone notification + watch haptic warn "App restrictions in 15 minutes"
- 5 min before block: second warning — phone notification + stronger watch haptic
- At block time: mock blocking UI activates — apps visually "shut down" in-app
- User taps notification → app opens →
ScienceModalViewshows study details - If user wants to opt out → Mindfulness Exit Flow (replaces snooze — see below)
There is no snooze button. If a user wants to disable the wind-down:
- User taps "I need more time" on TonightView
MindfulnessExitViewappears — full-screen, cannot be dismissed- Screen shows a calm mindfulness prompt (breathing exercise, reflection question)
- Countdown timer shows how long they must wait before opting out
- Escalating wait times (resets each night):
| Attempt | Wait Time | Free Window | Prompt Theme |
|---|---|---|---|
| 1st | 1 minute | 10 minutes | "Take 3 deep breaths. Is this really worth losing sleep over?" |
| 2nd | 3 minutes | 10 minutes | "Guided box breathing: 4 in, 4 hold, 4 out, 4 hold" |
| 3rd | 5 minutes | 5 minutes | "Think about how you'll feel tomorrow morning." |
| 4th+ | 10 minutes | 5 minutes | "You've opted out 3 times tonight. Your sleep debt is growing." |
- After wait completes → "Continue anyway" button appears
- Wind-down pauses, free window timer starts (visible on TonightView + Watch)
- When free window expires → wind-down resumes automatically, watch haptic fires
MindfulnessExitStatelogged to SwiftData (count, duration, time — for user self-awareness, not shaming)
- iPhone → Watch:
WatchSyncServicesendsNightPlanviatransferUserInfo - Watch displays simplified TonightView: countdown ring + next checklist item
- At each notification milestone:
HapticEnginefires appropriate haptic type - Haptic intensity decreases as bedtime approaches (calming, not alarming):
| Time to Bed | Haptic Type | Feel |
|---|---|---|
| > 2 hours | .notification |
Strong, attention-grabbing |
| 1–2 hours | .directionUp |
Medium, noticeable |
| 30 min–1 hour | .click |
Light tap |
| 15–30 min | .directionDown |
Gentle, subtle |
| < 15 min | .success |
Soft, reassuring |
| Wake-up alarm | .notification + .retry |
Strong, escalating |
- Watch can send "Start wind-down now" action → iPhone via
sendMessage - During free window (mindfulness exit): Watch shows countdown timer
- Morning notification fires ("Get sunlight in 30 minutes")
- Watch: wake-up haptic fires (
.notification— strong, to wake up) - App foregrounds →
HealthKitServicepulls last night's sleep data (Phase 2) SleepCalculatorcomputes sleep debt (actual vs. target)ProgressViewModelupdates weekly view with last night's resultsNotificationServiceschedules tonight's notifications (next night in plan)WatchSyncServicesends updated data to Watch
- HealthKit observer query runs in background for sleep analysis samples
- On new data:
HealthKitServicepublishes via Combine/async stream SleepRecordcreated and persisted- Sleep debt recalculated → UI updates
- Updated sleep debt synced to Watch
SleepGoal
├── id: UUID
├── currentBedtime: Date (time-of-day)
├── targetBedtime: Date (time-of-day)
├── durationDays: Int
├── startDate: Date
├── isActive: Bool
└── computed: nightly increment (minutes) = total delta / durationDays
NightPlan
├── id: UUID
├── date: Date
├── targetBedtime: Date
├── windDownStart: Date (targetBedtime - 4 hours)
├── notifications: [ScheduledNotification]
├── checklistItems: [ChecklistItem]
├── status: .upcoming | .active | .completed | .optedOut
├── mindfulnessExits: [MindfulnessExitEvent]
└── mockBlockingActive: Bool
MindfulnessExitEvent
├── id: UUID
├── timestamp: Date
├── attemptNumber: Int (1st, 2nd, 3rd...)
├── waitDuration: TimeInterval (how long they waited)
├── freeWindowDuration: TimeInterval (how much free time granted)
├── freeWindowUsed: TimeInterval (how much they actually used)
└── resumedAutomatically: Bool (true = timer expired, false = user returned early)
MindfulnessExitState (runtime, not persisted)
├── currentAttempt: Int
├── waitTimeRemaining: TimeInterval
├── freeWindowRemaining: TimeInterval?
├── phase: .waiting | .reflecting | .freeWindow | .inactive
└── computed: nextWaitDuration, nextFreeWindow
ScheduledNotification
├── id: UUID
├── offsetMinutes: Int (negative = before bedtime)
├── template: NotificationTemplate
├── fireDate: Date
├── status: .pending | .delivered | .interacted
NotificationTemplate
├── id: String (e.g., "stop_eating")
├── offsetMinutes: Int (-240 = 4 hrs before)
├── emoji: String
├── title: String
├── body: String
├── scienceReference: ScienceReference
SleepRecord
├── id: UUID
├── date: Date
├── actualBedtime: Date?
├── actualWakeTime: Date?
├── sleepDuration: TimeInterval?
├── sleepStages: [SleepStage]? (from HealthKit)
├── hrv: Double?
├── restingHeartRate: Double?
├── source: .healthKit | .manual | .estimated
ScienceReference
├── claim: String
├── studies: [Study]
└── struct Study
├── authors: String
├── year: Int
├── journal: String
├── finding: String
└── doi: String?
MockBlockedApp
├── id: UUID
├── name: String
├── iconName: String (SF Symbol or bundled asset)
├── tier: .blockFirst | .block30Min | .alwaysAllowed
├── isCurrentlyBlocked: Bool
└── blockedAt: Date?
ChecklistItem
├── id: String
├── title: String
├── description: String
├── offsetMinutes: Int
├── isCompleted: Bool
└── scienceReference: ScienceReference?
WatchPayload (Codable, sent via WatchConnectivity)
├── targetBedtime: Date
├── windDownStart: Date
├── nightNumber: Int
├── totalNights: Int
├── sleepDebt: TimeInterval (mocked in Phase 1)
├── currentChecklistItems: [ChecklistItem]
├── mockBlockingActive: Bool
└── mindfulnessExitState: MindfulnessExitState?
HapticScheduleEntry
├── fireDate: Date
├── hapticType: WKHapticType
├── repeatCount: Int (e.g., wake-up alarm repeats)
└── associatedNotificationId: String?
Goal: End-to-end wind-down experience with Apple Watch companion, mock app blocking UI, science-backed notifications, and mindfulness exit flow. No HealthKit, no Screen Time API. Mocked sleep debt. Real notifications and real Watch haptics.
- 1.1.1 Create Xcode project with two targets:
- iOS app (SwiftUI, iOS 17+, SwiftData)
- watchOS app (SwiftUI, watchOS 10+)
- 1.1.2 Set up folder structure per architecture above with
Shared/group for cross-target code - 1.1.3 Add capabilities:
UNUserNotifications(iOS)WatchConnectivity(both targets)
- 1.1.4 Create
Info.plistentries:NSUserNotificationsUsageDescription: "MoreSleep sends wind-down reminders at your target bedtime"
- 1.1.5 Create color theme and basic design tokens
- Dark-mode-first palette (sleep-themed deep blues, purples, warm ambers)
- Define
Color+Theme.swiftextension with semantic colors - Ensure colors work on both iOS and watchOS
- 1.2.1 Define
SleepGoalas SwiftData@Model(in Shared/)- Properties: id, currentBedtime, targetBedtime, durationDays, startDate, isActive
- 1.2.2 Define
NightPlanas SwiftData@Model(in Shared/)- Properties: id, date, targetBedtime, windDownStart, status, mindfulnessExits, mockBlockingActive
- Relationship: belongs to SleepGoal
- 1.2.3 Define
NotificationTemplateas plain Codable struct (in Shared/) - 1.2.4 Define
MindfulnessExitEventandMindfulnessExitState(in Shared/) - 1.2.5 Define
MockBlockedAppmodel with preset app list (in Shared/) - 1.2.6 Define
WatchPayloadCodable struct (in Shared/) - 1.2.7 Define
HapticScheduleEntrystruct (in Shared/) - 1.2.8 Create
ScienceData.jsonwith all 11 notification templates + study references - 1.2.9 Implement
SleepCalculatorservice (in Shared/):- 1.2.9.1
calculateNightlyPlans(from goal: SleepGoal) -> [NightPlan] - 1.2.9.2
tonightsTarget(from goal: SleepGoal, on date: Date) -> Date - 1.2.9.3
notificationSchedule(for bedtime: Date) -> [ScheduledNotification] - 1.2.9.4
hapticSchedule(for bedtime: Date) -> [HapticScheduleEntry] - 1.2.9.5
mindfulnessWaitTime(attemptNumber: Int) -> (wait: TimeInterval, freeWindow: TimeInterval)
- 1.2.9.1
- 1.3.1 Create
WatchSyncServiceprotocol:sendNightPlan(_ plan: WatchPayload) asyncsendMindfulnessUpdate(_ state: MindfulnessExitState) asynconWindDownRequested(_ handler: @escaping () -> Void)(watch → phone action)
- 1.3.2 Implement iOS-side
WatchSyncService:- 1.3.2.1 Activate
WCSessioninMoreSleepApp.init() - 1.3.2.2 Use
transferUserInfofor NightPlan sync (reliable, queued) - 1.3.2.3 Use
sendMessagefor real-time updates (mindfulness state, block state) - 1.3.2.4 Handle
session(_:didReceiveMessage:)for watch → phone actions
- 1.3.2.1 Activate
- 1.3.3 Implement watchOS-side
WatchSyncService:- 1.3.3.1 Activate
WCSessioninMoreSleepWatchApp.init() - 1.3.3.2 Handle
session(_:didReceiveUserInfo:)for NightPlan updates - 1.3.3.3 Handle
session(_:didReceiveMessage:)for real-time state changes - 1.3.3.4 Send "start wind-down" message to phone
- 1.3.3.1 Activate
- 1.3.4 Implement
HapticEngineservice (watchOS only):- 1.3.4.1
scheduleHaptics(for plan: WatchPayload)— maps notification offsets to haptic types - 1.3.4.2 Haptic type mapping by time-to-bed:
> 2h:.notification(strong, attention-grabbing)1-2h:.directionUp(medium, noticeable)30min-1h:.click(light tap)15-30min:.directionDown(gentle, subtle)< 15min:.success(soft, reassuring)- Wake-up:
.notificationrepeated 3x (strong, to wake)
- 1.3.4.3
fireHaptic(_ type: WKHapticType)— immediate haptic - 1.3.4.4
cancelAllScheduled()— clear pending haptics
- 1.3.4.1
- 1.3.5 Watch face complications:
- 1.3.5.1 Circular small: mocked sleep debt number
- 1.3.5.2 Modular large: bedtime countdown + sleep debt
- 1.3.5.3 Corner: moon icon with hours until bedtime
- 1.4.1 Create
NotificationServiceprotocol:requestPermission() async -> BoolscheduleNotifications(for plan: NightPlan) asynccancelAllPending() async
- 1.4.2 Implement
LiveNotificationServiceusingUNUserNotificationCenter:- 1.4.2.1 Permission request with proper error handling
- 1.4.2.2 Schedule using
UNCalendarNotificationTriggerwith exact date components - 1.4.2.3 Cancel by notification identifiers (grouped by night)
- 1.4.2.4 Schedule pre-block warnings at -15 min and -5 min before each blocking tier activates
- 1.4.3 Add notification categories:
- Category:
"winddown_reminder"with actions:"why"(opens science modal),"done"(marks checklist item) - Category:
"block_warning"with action:"ok"(acknowledge) - No snooze action — removed entirely
- Category:
- 1.4.4 Implement
UNUserNotificationCenterDelegate:- Handle
"why"action → set app state to show science modal - Handle
"done"action → mark corresponding checklist item complete
- Handle
- 1.5.1
WelcomeView:- App name + tagline
- Brief pitch (2-3 sentences)
- "Get Started" button → navigates to BedtimeGoalView
- 1.5.2
BedtimeGoalView:- 1.5.2.1
DatePicker(time only) for "When do you currently go to bed?" - 1.5.2.2
DatePicker(time only) for "When do you want to go to bed?" - 1.5.2.3
Stepperfor "Over how many days?" (range: 3-30, default: 7) - 1.5.2.4 Live preview: "Your bedtime will shift by X minutes per night"
- 1.5.2.5 Validation: target must be earlier than current, minimum 15-min shift per night
- 1.5.2.1
- 1.5.3
SetupGuideView:- 1.5.3.1 Night Shift card with deep link + checkbox
- 1.5.3.2 Sleep Focus card with deep link + checkbox
- 1.5.3.3 "Skip for now" link at bottom
- 1.5.4
PermissionsView:- 1.5.4.1 Notification permission request
- 1.5.4.2 Success/failure state display
- 1.5.4.3 "Continue" → saves goal, generates plans, schedules notifications, syncs to Watch, navigates to TonightView
- 1.6.1
TonightView— main screen layout:- 1.6.1.1
CountdownRing— large, centered, animated circular countdown - 1.6.1.2 Tonight's target bedtime displayed prominently (e.g., "11:45 PM")
- 1.6.1.3 "Night X of Y" progress badge
- 1.6.1.4 Scrollable wind-down checklist:
- Each item: emoji, title, scheduled time, completion state
- Items unlock as their time arrives
- 1.6.1.5 "I need more time" button (replaces snooze):
- Styled to be intentionally inconspicuous — small text, bottom of screen, muted color
- Tapping opens
MindfulnessExitView
- 1.6.1.1
- 1.6.2
CountdownRing:- 1.6.2.1 Circular arc filling clockwise (0% at wind-down start → 100% at bedtime)
- 1.6.2.2 Color: green (>2h) → yellow (1-2h) → orange (30min-1h) → red (<30min)
- 1.6.2.3 Updates every second via
TimelineView(.periodic(every: 1)) - 1.6.2.4 Center text: hours and minutes remaining
- 1.6.3
ChecklistRow:- 1.6.3.1 Emoji + title + scheduled time
- 1.6.3.2 States: locked (greyed), active (highlighted), done (checked)
- 1.6.3.3 Tap → toggle done
- 1.6.3.4 "Why?" trailing button →
ScienceModalView
- 1.6.4
ScienceModalView(.sheet):- 1.6.4.1 Emoji + claim header
- 1.6.4.2 Plain-language science explanation
- 1.6.4.3 Study citations with authors, year, journal, key finding
- 1.6.4.4 "Read full study" DOI link
- 1.6.4.5 "Got it" dismiss button
- 1.7.1
MindfulnessExitView— full-screen cover (cannot swipe to dismiss):- 1.7.1.1 Calm, dark UI with soft animations (breathing circle, gentle pulse)
- 1.7.1.2 Reflection prompt that changes per attempt:
- 1st: "Take 3 deep breaths. Is this really worth losing sleep over?"
- 2nd: Guided box breathing animation (4-4-4-4 seconds)
- 3rd: "Think about how you'll feel tomorrow morning."
- 4th+: "You've opted out X times tonight. Your sleep debt is growing."
- 1.7.1.3 Countdown timer — large, centered, counting down wait time
- 1.7.1.4 "Continue anyway" button — hidden until timer reaches zero
- 1.7.1.5 "Never mind, keep wind-down" button — always visible, returns to TonightView
- 1.7.2 Free window state:
- 1.7.2.1 When "Continue anyway" tapped: wind-down pauses
- 1.7.2.2 TonightView shows free window countdown (e.g., "Apps available for 8:32")
- 1.7.2.3 Watch shows matching free window countdown
- 1.7.2.4 Notification at 2 min before window expires: "Wind-down resuming soon"
- 1.7.2.5 When window expires: wind-down resumes automatically, watch haptic fires
- 1.7.3 Escalation logic:
- 1.7.3.1 Attempt counter stored on
NightPlan.mindfulnessExits - 1.7.3.2 Wait times: 1 min → 3 min → 5 min → 10 min (4th+)
- 1.7.3.3 Free windows: 10 min → 10 min → 5 min → 5 min (4th+)
- 1.7.3.4 Counter resets to 0 at start of each new night
- 1.7.3.1 Attempt counter stored on
- 1.8.1
MockAppTierView— app categorization screen:- 1.8.1.1 Grid of preset "apps" (Instagram, TikTok, YouTube, Messages, Phone, etc.) using SF Symbols or bundled icons
- 1.8.1.2 Three drop zones: "Block first" / "Block 30 min before" / "Always allowed"
- 1.8.1.3 Drag-and-drop to assign tiers
- 1.8.1.4 Default suggestions pre-populated
- 1.8.1.5 Persist assignments in SwiftData
- 1.8.2
MockBlockedAppsView— the "hero" screen during wind-down:- 1.8.2.1 Grid showing all "apps" the user categorized
- 1.8.2.2 As wind-down progresses, apps in active tiers visually "shut down":
- Icon dims + grayscale filter
AppShieldOverlayslides over the icon- Subtle animation (fade or slide down)
- 1.8.2.3 Apps shut down one by one with staggered timing (not all at once) — 3-5 second delay between each for dramatic effect
- 1.8.2.4 Tier 1 apps block at wind-down start
- 1.8.2.5 Tier 2 apps block 30 min before bedtime
- 1.8.2.6 "Always allowed" apps stay lit throughout
- 1.8.3
AppShieldOverlay:- 1.8.3.1 Dark overlay with moon icon
- 1.8.3.2 Text: "This app is sleeping. You should be too."
- 1.8.3.3 Small text: "Unlocks at [wake time]"
- 1.8.3.4 Sleep-themed color scheme matching app theme
- 1.9.1
WatchTonightView:- 1.9.1.1 Compact countdown ring (adapted for small screen)
- 1.9.1.2 Target bedtime text
- 1.9.1.3 Night X of Y badge
- 1.9.1.4 Next upcoming checklist item (just one, not full list)
- 1.9.1.5 "Start wind-down now" button → sends message to iPhone
- 1.9.2
WatchWindDownView— shown during active wind-down:- 1.9.2.1 Simplified countdown
- 1.9.2.2 Current checklist item
- 1.9.2.3 "Put your phone down" message when close to bedtime
- 1.9.2.4 Free window countdown (if mindfulness exit active)
- 1.9.3 Haptic behavior:
- 1.9.3.1 Fires at each notification milestone (matching phone notifications)
- 1.9.3.2 Decreasing intensity as bedtime approaches (see haptic table above)
- 1.9.3.3 Pre-block warnings: 15 min and 5 min before each tier blocks
- 1.9.3.4 Wake-up: strong
.notificationhaptic repeated 3x
- 1.10.1 Design and add app icon (moon/crescent/sleep themed)
- 1.10.2 Add branded launch screen
- 1.10.3 Create "demo mode" configuration:
- Flag that compresses timeline (4 hours → 4 minutes)
- Notifications fire every 20-30 seconds
- Mock blocking activates visibly with staggered shutdowns
- Watch haptics fire at compressed intervals
- Mindfulness exit wait times compressed (60s → 5s)
- 1.10.4 Test complete flow on physical iPhone + Apple Watch
- 1.10.5 Prepare fallback: screen recording in case live demo has issues
Goal: Replace mocked data with real HealthKit sleep tracking, compute actual sleep debt, and build a progress dashboard.
- 2.1.1 Add HealthKit capability and entitlement
- 2.1.2 Add
Info.plistkeys for health data usage descriptions - 2.1.3 Create
HealthKitServiceprotocol - 2.1.4 Implement authorization for: sleep analysis, heart rate, HRV, resting HR, SpO2, respiratory rate, body temperature, steps, active energy, mindful minutes
- 2.1.5 Register background delivery for sleep analysis
- 2.2.1 Create
SleepRecordSwiftData model - 2.2.2 Query HealthKit for last night's sleep analysis
- 2.2.3 Parse sleep stages (awake, REM, core, deep)
- 2.2.4 Compute actual bedtime, wake time, sleep efficiency
- 2.2.5 Display sleep quality score
- 2.3.1 Replace mocked sleep debt with HealthKit-derived values
- 2.3.2 Compute rolling 7-day and 14-day sleep debt
- 2.3.3 Sync real sleep debt to Watch complications
- 2.3.4 Display prominently on Tonight screen and Progress screen
- 2.4.1 Weekly bar chart: sleep duration vs. target
- 2.4.2 Bedtime consistency graph: planned vs. actual
- 2.4.3 Health metrics cards (HRV, resting HR, SpO2 trends)
- 2.4.4 Sleep debt trend line
- 2.4.5 Streak counter with reset on missed target
- 2.4.6 Mindfulness exit history: "You opted out X times this week"
Goal: Replace mock blocking UI with real Screen Time API integration.
- 3.1.1 Add Family Controls + App Groups capabilities
- 3.1.2 Request Screen Time authorization
- 3.1.3 Implement
ScreenTimeServiceusingManagedSettingsStore - 3.1.4 Create
ShieldConfigurationDataSourceextension (custom block screen matching mock UI design)
- 3.2.1 Replace mock app grid with
FamilyActivityPicker - 3.2.2 Progressive blocking engine via
DeviceActivityMonitorextension - 3.2.3 Real shield screens matching the mock design from Phase 1
- 3.3.1 Mindfulness exit now actually removes
ManagedSettingsStoreshields - 3.3.2 Free window timer re-applies shields when expired
- 3.3.3 Track unlock events in SwiftData
- 3.4.1 Programmatic Sleep Focus scheduling
- 3.4.2 Detect manual Sleep Focus activation → trigger early wind-down
- 3.4.3 Communication limits during wind-down
Goal: Siri, CloudKit, advanced sensors, platform expansion.
- 4.1.1 "Start my wind-down routine"
- 4.1.2 "Set tonight's bedtime to [time]"
- 4.1.3 "How's my sleep debt?" (spoken response)
- 4.1.4 "Log I'm going to bed"
- 4.1.5 Personal automation: charger plugged in → start wind-down
- 4.2.1 Sync goals, plans, records across devices
- 4.2.2 iCloud backup of sleep history
- 4.2.3 Shared goals for accountability
- 4.3.1 Core Motion: phone face-down detection
- 4.3.2 Screen Distance API: alert if phone too close during wind-down
- 4.3.3 Night Shift / Grayscale automation setup flows
- 4.3.4 Adaptive notifications (learn engagement, retire internalized habits)
- 4.3.5 Smart bedtime suggestions from health data trends
- iOS 17.0 — required for SwiftData and
@Observable - watchOS 10.0 — required for modern SwiftUI watch layouts
| Feature | Framework | Key Classes | Target |
|---|---|---|---|
| Notifications | UserNotifications | UNUserNotificationCenter, UNCalendarNotificationTrigger |
iOS |
| Watch sync | WatchConnectivity | WCSession, WCSessionDelegate |
iOS + watchOS |
| Watch haptics | WatchKit | WKInterfaceDevice, WKHapticType |
watchOS |
| Watch complications | ClockKit / WidgetKit | CLKComplicationDataSource or WidgetFamily |
watchOS |
| Health data | HealthKit | HKHealthStore, HKSampleQuery, HKObserverQuery |
iOS (Phase 2) |
| App blocking | FamilyControls + ManagedSettings | AuthorizationCenter, ManagedSettingsStore |
iOS (Phase 3) |
| Focus modes | Intents | INFocusStatusCenter, deep links |
iOS (Phase 2+) |
| Persistence | SwiftData | @Model, ModelContainer, ModelContext |
iOS |
| Shortcuts | AppIntents | AppIntent, AppShortcutsProvider |
iOS (Phase 4) |
| Permission | Info.plist Key | When Requested |
|---|---|---|
| Notifications | NSUserNotificationsUsageDescription |
Onboarding |
| (none for Watch — paired automatically via WatchConnectivity) |
transferUserInfo: Used for NightPlan sync — reliable, survives app termination, queuedsendMessage: Used for real-time updates — mindfulness exit state, block state changes, "start wind-down" actionupdateApplicationContext: Used for latest sleep debt / tonight summary — watch reads on launch- Fallback: If watch app launches without phone context, show "Open MoreSleep on iPhone" message
- watchOS does not support scheduled haptics in the background
- Strategy: Use local notifications on the watch (
UNUserNotificationCenteron watchOS) as triggers, then fire haptic inuserNotificationCenter(_:willPresent:)delegate - Alternative: Keep watch app in foreground with a
TimelineViewthat checks against haptic schedule and fires at correct times - Wake-up haptic: Schedule as a watchOS local notification with sound, which triggers haptic on delivery
- iOS limit: 64 pending local notifications maximum
- Strategy: On each app launch, cancel stale, schedule next 7 nights
- Trigger type:
UNCalendarNotificationTriggerwith exactDateComponents - Categories:
"winddown_reminder": actions:"why"(foreground, opens science modal),"done"(background, marks checklist)"block_warning": actions:"ok"(dismiss acknowledgement)
- No snooze action in any category
- Pre-block warnings: scheduled at -15 min and -5 min before each tier activation time
MockBlockedAppstores a preset list of fake apps with SF Symbol icons- No system-level blocking — purely visual within our app
MockBlockedAppsViewuses SwiftUI animations to show apps "shutting down"- Timer-driven:
TimelineViewchecks current time against tier block times - Staggered animation: 3-5 second delay between each app blocking for dramatic effect
- Designed to match what the real Screen Time shield will look like in Phase 3
Pre-setup: Demo mode enabled — timeline compressed to minutes. Apple Watch paired and on wrist.
| Time | What to Show | Script |
|---|---|---|
| 0:00 – 0:30 | Open app → Onboarding | "You tell MoreSleep your current bedtime and your goal. It builds a personalized plan — midnight to 10:30 PM over 7 nights, shifting 15 minutes per night." |
| 0:30 – 1:00 | Tonight screen + Watch | "Tonight's target: 11:45 PM. Countdown on your phone AND your wrist. Your Watch will tap you at every milestone." |
| 1:00 – 1:30 | Notification fires + Watch haptic | "Phone notification: 'Last meal window closing.' Simultaneously, your Watch taps your wrist. Every reminder backed by peer-reviewed research." |
| 1:30 – 2:00 | Tap "Why?" → Science modal | "Tap 'Why?' — Nakajima et al., 2020: eating within 3 hours of bed shifts your clock 1.5 hours. Full DOI link included." |
| 2:00 – 2:30 | App tier list | "Drag apps into tiers. Instagram, TikTok — block first. Messages — 30 min before bed. Phone — always allowed." |
| 2:30 – 3:30 | HERO MOMENT: Apps shut down one by one | "Wind-down starts. Watch taps — Instagram: sleeping. [pause] TikTok: sleeping. [pause] YouTube: gone. Each app shows 'This app is sleeping. You should be too.' Your phone is going to bed WITH you." |
| 3:30 – 4:15 | Mindfulness exit flow | "What if you NEED your apps? No snooze button. You wait. 1 minute of guided breathing. 'Is this really worth losing sleep over?' Only then can you opt out — for 10 minutes. Second time? 3-minute wait. It gets harder each time." |
| 4:15 – 4:30 | Watch shows decreasing haptics | "Notice: the Watch haptics get gentler as bedtime approaches. Strong taps early, soft nudges at the end. Calming you down, not alarming you." |
| 4:30 – 5:00 | Morning: wake-up haptic + sunlight reminder | "Morning: strong Watch haptic wakes you. 'Get sunlight in 30 minutes' — Stanford research. Sleep debt is dropping. Night 3 of 7." |
Hero moments:
- Apps visually shutting down one by one (2:30-3:30)
- Mindfulness exit replacing snooze — friction by design (3:30-4:15)
- Watch haptics that calm down as night deepens (4:15-4:30)
The gradual "shutting down" of apps is visible, dramatic, and satisfying, paired with Watch haptics that transition from alerting to calming. There is no easy escape — opting out requires genuine mindful reflection.
Core Loop:
- User can set a bedtime goal in under 30 seconds
- Tonight screen shows accurate, live countdown to target bedtime
- Bedtime target advances by correct increment each night
- Demo mode compresses the full wind-down into under 5 minutes
Notifications:
- At least 5 unique science-backed notifications fire at correct times
- Each notification has a working "Why?" button that opens study citations
- Pre-block warnings fire at 15 min and 5 min before tier activation
- No snooze action exists anywhere in the app
Apple Watch:
- Watch receives and displays tonight's plan via WatchConnectivity
- Watch fires haptics at each notification milestone
- Haptic type decreases in intensity as bedtime approaches
- Wake-up haptic fires strongly in the morning
- Watch complications show bedtime countdown and mocked sleep debt
- "Start wind-down now" button on Watch sends action to phone
Mock App Blocking:
- User can drag mock apps into 3 tiers
- Tier 1 apps visually block at wind-down start
- Tier 2 apps visually block 30 min before bedtime
- Apps shut down one-by-one with staggered animation (not all at once)
- Custom shield overlay appears on "blocked" apps
- "Always allowed" apps stay unblocked throughout
Mindfulness Exit:
- "I need more time" button is present but inconspicuous
- Tapping it opens full-screen mindfulness view with countdown
- Wait time escalates: 1 min → 3 min → 5 min → 10 min
- Free window decreases: 10 min → 10 min → 5 min → 5 min
- "Continue anyway" button only appears after timer completes
- "Never mind" option always available to return to wind-down
- Escalation resets each night
- Wind-down auto-resumes when free window expires
Overall:
- Full wind-down → bedtime → morning sequence tells a compelling story
- Every notification is traceable to a peer-reviewed study
- App feels calm and intentional, not punitive
- Analyze HealthKit data patterns to suggest optimal personal bedtime
- Correlate notification compliance with sleep quality improvement over time
- Personalize notification timing based on user behavior patterns
- Predict sleep debt recovery timeline: "At this rate, you'll clear your debt in 4 days"
- Detect weekday vs. weekend patterns and adapt accordingly
- Track engagement per notification type: tapped, dismissed, ignored
- A/B test notification copy variations (rotate phrasing weekly)
- Gradually retire notifications for internalized habits
- Surface new recommendations based on specific sleep issues
- Share sleep goals with friends/partners
- "Sleep challenge" — compete on bedtime consistency
- Accountability groups with anonymized weekly progress
- Partner mode: "Your partner hit their bedtime target tonight"
- Correlate caffeine intake with sleep onset latency
- Correlate exercise timing with sleep quality
- Weekly "Sleep Insights" report with personalized findings
- Detect patterns suggesting potential sleep disorders → suggest consultation
- iPad companion: detailed analytics dashboard
- Mac app: Screen Time sync, menu bar countdown widget
- Widgets: Lock Screen countdown, Home Screen sleep debt
- Live Activities: wind-down progress on Dynamic Island and Lock Screen
Full notification timeline working backwards from target bedtime:
| Offset | Time (11 PM example) | Emoji | Title | Key Science |
|---|---|---|---|---|
| -4h | 7:00 PM | 🍽️ | Last meal window closing soon | Late eating shifts circadian clock by 1.5h |
| -3h | 8:00 PM | 🚫 | Stop eating now | Body needs 3h to digest before sleep |
| -2.5h | 8:30 PM | 💧 | Last water for the night | Fluids 2h before bed increase wake-ups 40% |
| -2h | 9:00 PM | 💡 | Switch to warm lighting | Blue light suppresses melatonin by 85% |
| -90min | 9:30 PM | 📵 | Wind-down is starting | Melatonin window opens in 90 min |
| -1h | 10:00 PM | 📱 | Screens off | Screen light delays sleep 1-3 hours |
| -45min | 10:15 PM | 📖 | Time to read | 6 min of reading reduces stress by 68% |
| -30min | 10:30 PM | 🌡️ | Cool your room down | Ideal sleep temp: 65-68°F (18-20°C) |
| -15min | 10:45 PM | 🌙 | Almost bedtime | Melatonin is peaking, dim everything |
| 0 | 11:00 PM | 😴 | Bedtime. You got this. | Night X of goal complete |
| +8h | 7:00 AM | ☀️ | Get sunlight in 30 minutes | Morning light resets circadian clock |
- Nakajima et al. (2020) — Nutrients — Eating within 3h of bedtime associated with significantly worse sleep quality. Late eating delays circadian clock by up to 1.5 hours.
doi.org/10.3390/nu12010095 - Crispim et al. (2011) — Journal of Clinical Sleep Medicine — High caloric intake at night correlated with longer sleep onset and more wake-ups.
doi.org/10.5664/JCSM.1476
- Abal et al. (2019) — Sleep Medicine Reviews — Nocturia is the #1 cause of sleep fragmentation in adults. Fluid restriction 2h before bed reduces nighttime wake-ups by 40%.
- Asplund (1995) — Scandinavian Journal of Urology — Evening fluid intake directly correlated with number of nighttime awakenings.
- Chang et al. (2014) — PNAS — iPad vs. printed book: iPad readers took 10 min longer to fall asleep, had 90 min less REM sleep, felt less alert next morning.
doi.org/10.1073/pnas.1418490112 - Czeisler et al. (2015) — Harvard Medical School — Blue light suppresses melatonin for twice as long as green light. Shifts circadian rhythm by up to 3 hours.
- Dr. David Lewis (2009) — University of Sussex — 6 minutes of reading reduced stress by 68%. More effective than music (61%), walking (42%), or tea (54%).
- Elphinston & Noller (2011) — Journal of Applied Biobehavioral Research — Fiction readers showed greater ability to mentally escape. Readers fell asleep 26 min faster than non-readers.
- Zhao et al. (2012) — Journal of Athletic Training — 14 nights of red light therapy significantly improved sleep quality and melatonin secretion.
doi.org/10.4085/1062-6050-47.6.08 - Figueiro et al. (2011) — Neuroendocrinology Letters — Red wavelength (620-700nm) does not suppress melatonin. Blue (446-477nm) suppresses melatonin by up to 85%.
- Walker (2017) — Why We Sleep — Core body temp must drop 2-3°F to initiate sleep. Ideal: 65-68°F (18-20°C).
- Okamoto-Mizuno & Mizuno (2012) — Journal of Physiological Anthropology — Thermal environment is one of the most significant factors affecting sleep quality.
doi.org/10.1186/1880-6805-31-14
- Lewy et al. (2006) — PNAS — Morning light directly sets the phase of the circadian clock for the next 24 hours.
doi.org/10.1073/pnas.0601091103 - Huberman (2021) — Stanford / Huberman Lab — 10 min of morning sunlight within 30 min of waking sets circadian rhythm. Directly affects cortisol timing and melatonin onset.