Nouvelle target "CoachLiveActivity" (Widget Extension, bundle ch.hypnotruck.coach.LiveActivity, iOS 17.0) embarquée dans App via copy phase "Embed App Extensions" (dstSubfolderSpec=13). API ActivityKit + WidgetKit vérifiées sur SDK iOS 26.5 swiftinterface (Activity.request(content:), ActivityConfiguration, DynamicIsland(expanded:compactLeading:trailing:minimal:)). Architecture : - CoachLiveActivityAttributes (modèle partagé App ↔ extension) - CoachLiveActivityWidget (Lock Screen + Dynamic Island UI) - CoachLiveActivityBundle (@main WidgetBundle) - LiveActivityManager (App side, wrap Activity.request/update/end) - LiveStore hook : start sur 1er sample, update à chaque ingest, end après 60s "lost". Tout guardé @available(iOS 16.2, *) pour préserver deployment iOS 15. - Info.plist App : NSSupportsLiveActivities = YES. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
24 lines
903 B
Swift
24 lines
903 B
Swift
// CoachLiveActivityAttributes.swift
|
|
// Modèle partagé App ↔ Widget Extension (membre des 2 targets).
|
|
// Définit ce que la Live Activity / Dynamic Island affiche.
|
|
|
|
import ActivityKit
|
|
import Foundation
|
|
|
|
@available(iOS 16.2, *)
|
|
struct CoachLiveActivityAttributes: ActivityAttributes {
|
|
/// Données dynamiques (mises à jour à chaque sample reçu de la Watch).
|
|
public struct ContentState: Codable, Hashable {
|
|
var heartRate: Double
|
|
var activeEnergyKcal: Double
|
|
var distanceMeters: Double
|
|
var elapsedSec: TimeInterval
|
|
var isLive: Bool // true = direct ; false = perdu/en pause
|
|
}
|
|
|
|
/// Données fixes (set au start, non modifiables).
|
|
var activityLabel: String // "Course", "Vélo", "Marche", "Renfo", ou "Séance"
|
|
var activitySymbol: String // SF Symbol (figure.run, bicycle, figure.walk, dumbbell)
|
|
var startedAt: Date
|
|
}
|