Phase 4.A.1: Live Activity + Dynamic Island für laufende Gassi-Tour
Neues Widget-Extension-Target BanYaroGoWidgetExtension: - Bundle-ID app.banyaro.ios.BanYaroGoWidget - NSExtensionPointIdentifier = com.apple.widgetkit-extension - Synced root group + explizite Info.plist + Embed-Phase in App-Target - Cross-Membership der Shared/WalkActivityAttributes.swift in beiden Targets Shared/WalkActivityAttributes.swift: - ActivityAttributes mit startedAt (fix) - ContentState mit distanceMeters, elapsedSeconds, pointCount, isPaused, isAutoPaused BanYaroGoWidget/WalkLiveActivity.swift: - Lock-Screen-View: Pfote-Icon + Status-Pille (Live/Pause/Auto-Pause) + Stats-Spalten (Distanz/Dauer/Punkte) - Dynamic Island compact: Pfote leading, Distanz trailing - Dynamic Island minimal: nur Pfote - Dynamic Island expanded: Distanz/Dauer/Status mit Pfote zentriert WalkActivityController: - @MainActor Facade um ActivityKit - start() prüft areActivitiesEnabled, killt orphaned current, request mit Initial-State - update() async via Task - end() mit dismissalPolicy.immediate TrackingView: - .onChange(of: tracker.isTracking) → Start/End - persistTicker (5s) → update - .onChange(of: tracker.isPaused/isAutoPaused) → sofort update für saubere UX BanYaroGo-Info.plist: NSSupportsLiveActivities = true
This commit is contained in:
parent
fec7c79b05
commit
500a645bfd
8 changed files with 458 additions and 0 deletions
41
BanYaroGo/Tracking/WalkActivityController.swift
Normal file
41
BanYaroGo/Tracking/WalkActivityController.swift
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import Foundation
|
||||
import ActivityKit
|
||||
|
||||
/// Tiny facade around ActivityKit so TrackingView doesn't have to know the
|
||||
/// details. There is at most one walk activity alive at any time.
|
||||
@MainActor
|
||||
enum WalkActivityController {
|
||||
private static var current: Activity<WalkActivityAttributes>?
|
||||
|
||||
static func start(startedAt: Date, initialState: WalkActivityAttributes.WalkActivityState) {
|
||||
guard ActivityAuthorizationInfo().areActivitiesEnabled else { return }
|
||||
// Kill anything left over from a previous (orphaned) session.
|
||||
if current != nil { end() }
|
||||
let attributes = WalkActivityAttributes(startedAt: startedAt)
|
||||
let content = ActivityContent(state: initialState, staleDate: nil)
|
||||
do {
|
||||
current = try Activity.request(
|
||||
attributes: attributes,
|
||||
content: content,
|
||||
pushType: nil
|
||||
)
|
||||
} catch {
|
||||
print("Live Activity request failed: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
static func update(_ state: WalkActivityAttributes.WalkActivityState) {
|
||||
let activity = current
|
||||
Task { @MainActor in
|
||||
await activity?.update(ActivityContent(state: state, staleDate: nil))
|
||||
}
|
||||
}
|
||||
|
||||
static func end() {
|
||||
let activity = current
|
||||
current = nil
|
||||
Task { @MainActor in
|
||||
await activity?.end(nil, dismissalPolicy: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue