feat: iOS widgets + fix sleep showing yesterday's data
Widgets: - HabitsProgressWidget (small/medium): progress ring, completed/total habits, tasks count - HealthSummaryWidget (small/medium): readiness score, steps, sleep, heart rate - Shared Keychain access group for app ↔ widget token sharing - Widget data refreshes every 30 minutes Sleep fix: - Changed sleep window from "24 hours back" to "6 PM yesterday → now" - Captures overnight sleep correctly without showing previous day's data - Applied to both fetchSleepData (sync) and fetchSleepSegments (detail view) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -154,10 +154,16 @@ class HealthKitService: ObservableObject {
|
||||
private func fetchSleepData(dateFormatter: DateFormatter) async -> [[String: Any]] {
|
||||
guard let sleepType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis) else { return [] }
|
||||
|
||||
// Берём последние 24 часа, чтобы захватить ночной сон
|
||||
// Ночной сон: с 18:00 вчера до сейчас (захватывает засыпание вечером + пробуждение утром)
|
||||
let now = Date()
|
||||
guard let yesterday = Calendar.current.date(byAdding: .hour, value: -24, to: now) else { return [] }
|
||||
let sleepPredicate = HKQuery.predicateForSamples(withStart: yesterday, end: now)
|
||||
let cal = Calendar.current
|
||||
var startComponents = cal.dateComponents([.year, .month, .day], from: now)
|
||||
startComponents.hour = 18
|
||||
startComponents.minute = 0
|
||||
guard let todayEvening = cal.date(from: startComponents),
|
||||
let yesterdayEvening = cal.date(byAdding: .day, value: -1, to: todayEvening) else { return [] }
|
||||
let sleepStart = now.timeIntervalSince(todayEvening) > 0 ? todayEvening : yesterdayEvening
|
||||
let sleepPredicate = HKQuery.predicateForSamples(withStart: sleepStart, end: now)
|
||||
|
||||
return await withCheckedContinuation { cont in
|
||||
let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
|
||||
@@ -328,8 +334,13 @@ class HealthKitService: ObservableObject {
|
||||
func fetchSleepSegments() async -> [SleepSegment] {
|
||||
guard let sleepType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis) else { return [] }
|
||||
let now = Date()
|
||||
guard let yesterday = Calendar.current.date(byAdding: .hour, value: -24, to: now) else { return [] }
|
||||
let predicate = HKQuery.predicateForSamples(withStart: yesterday, end: now)
|
||||
let cal = Calendar.current
|
||||
var startComp = cal.dateComponents([.year, .month, .day], from: now)
|
||||
startComp.hour = 18
|
||||
guard let todayEvening = cal.date(from: startComp),
|
||||
let yesterdayEvening = cal.date(byAdding: .day, value: -1, to: todayEvening) else { return [] }
|
||||
let sleepStart = now.timeIntervalSince(todayEvening) > 0 ? todayEvening : yesterdayEvening
|
||||
let predicate = HKQuery.predicateForSamples(withStart: sleepStart, end: now)
|
||||
|
||||
return await withCheckedContinuation { cont in
|
||||
let sort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
|
||||
|
||||
Reference in New Issue
Block a user