fix: move Live Activity ownership to ViewModel, fix timer-at-0 and background freeze
**Architecture (PlayerViewModel):**
- Move ActivityKit lifecycle from SwiftUI View to ViewModel (MVVM correction)
- call syncActivity() at END of enterPhase() — after all state is set,
eliminating the race where phase was Published before timeRemaining
- Always recalculate phaseEndDate = Date() + timeRemaining (no stale cache)
- Dedicated Timer in ViewModel for periodic heart-rate/track sync (5s)
- Start/stop activity sync timer on play/pause/resume/abandon/finish
- stale activity reference discard + recreate-on-failure fallback
- Modern iOS 16.2+ API: ActivityContent, non-throwing update()
**PlayerView:**
- Remove all ActivityKit code (import, @State workoutActivity,
phaseEndDate, dynamicIslandAvailable, 4 methods, .onReceive timer)
- Delegate to ViewModel: onChange(musicVM.currentTrack) sets vm.trackTitle/Artist
and calls vm.syncActivity(); onDisappear calls await vm.endActivity()
- Music/audio onChange handlers no longer contain activity logic
**Info.plist:**
- Add UIBackgroundModes → audio so music continues and app stays alive
in background, allowing Timer-based activity updates
- Widget Info.plist: add NSSupportsLiveActivitiesFrequentUpdates
**WorkoutActivityAttributes.ContentState:**
- Add Sendable conformance for Swift 6 strict concurrency
Fixes: timer stuck at 0 on first work phase, exercise name missing,
music stopping in background, Dynamic Island freezing in background,
widget drift due to cached phaseEndDate
This commit is contained in:
@@ -2,7 +2,7 @@ import ActivityKit
|
||||
import Foundation
|
||||
|
||||
struct WorkoutActivityAttributes: ActivityAttributes {
|
||||
public struct ContentState: Codable, Hashable {
|
||||
public struct ContentState: Codable, Hashable, Sendable {
|
||||
var exerciseName: String
|
||||
var phase: String
|
||||
var phaseEndDate: Date
|
||||
|
||||
Reference in New Issue
Block a user