fix: widgets use App Group shared UserDefaults instead of Keychain
- Widgets can't access app's Keychain (different sandbox) - App writes data to shared UserDefaults (group.com.daniil.pulsehealth) - Widgets read from shared UserDefaults — no API calls needed - WidgetDataService: updates widget data + reloads timelines - DashboardView: pushes habits/tasks data to widget after load - HealthView: pushes health data to widget after load - App Group capability added to both app and widget entitlements - Widgets update every 15 minutes from cached data Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,9 +6,9 @@
|
||||
<true/>
|
||||
<key>com.apple.developer.healthkit.background-delivery</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.daniil.pulsehealth.shared</string>
|
||||
<string>group.com.daniil.pulsehealth</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
25
PulseHealth/Services/WidgetDataService.swift
Normal file
25
PulseHealth/Services/WidgetDataService.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import WidgetKit
|
||||
|
||||
enum WidgetDataService {
|
||||
static let suiteName = "group.com.daniil.pulsehealth"
|
||||
|
||||
static var shared: UserDefaults? {
|
||||
UserDefaults(suiteName: suiteName)
|
||||
}
|
||||
|
||||
static func updateHabits(completed: Int, total: Int, tasksCount: Int) {
|
||||
shared?.set(completed, forKey: "w_habits_completed")
|
||||
shared?.set(total, forKey: "w_habits_total")
|
||||
shared?.set(tasksCount, forKey: "w_tasks_count")
|
||||
WidgetCenter.shared.reloadTimelines(ofKind: "HabitsProgress")
|
||||
}
|
||||
|
||||
static func updateHealth(steps: Int, sleep: Double, heartRate: Int, readiness: Int) {
|
||||
shared?.set(steps, forKey: "w_steps")
|
||||
shared?.set(sleep, forKey: "w_sleep")
|
||||
shared?.set(heartRate, forKey: "w_heart_rate")
|
||||
shared?.set(readiness, forKey: "w_readiness")
|
||||
WidgetCenter.shared.reloadTimelines(ofKind: "HealthSummary")
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,11 @@ struct DashboardView: View {
|
||||
}
|
||||
todayHabits = habits
|
||||
isLoading = false
|
||||
|
||||
// Update widget
|
||||
let completed = habits.filter { $0.completedToday == true }.count
|
||||
let activeTasks = todayTasks.filter { !$0.completed }.count
|
||||
WidgetDataService.updateHabits(completed: completed, total: habits.count, tasksCount: activeTasks)
|
||||
}
|
||||
|
||||
func filterHabitsForToday(_ habits: [Habit]) -> [Habit] {
|
||||
|
||||
@@ -200,6 +200,14 @@ struct HealthView: View {
|
||||
async let h = HealthAPIService.shared.getHeatmap(days: 7)
|
||||
readiness = try? await r; latest = try? await l; heatmapData = (try? await h) ?? []
|
||||
isLoading = false
|
||||
|
||||
// Update widget
|
||||
WidgetDataService.updateHealth(
|
||||
steps: latest?.steps?.total ?? 0,
|
||||
sleep: latest?.sleep?.totalSleep ?? 0,
|
||||
heartRate: Int(latest?.restingHeartRate?.value ?? 0),
|
||||
readiness: readiness?.score ?? 0
|
||||
)
|
||||
}
|
||||
|
||||
func syncHealthKit() async {
|
||||
|
||||
Reference in New Issue
Block a user