Files
Ascently/ios/OpenClimb/ViewModels/LiveActivityManager.swift

147 lines
5.1 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import ActivityKit
import Foundation
@MainActor
final class LiveActivityManager {
static let shared = LiveActivityManager()
private init() {}
private var currentActivity: Activity<SessionActivityAttributes>?
/// Check if there's an active session and restart Live Activity if needed
func restartLiveActivityIfNeeded(activeSession: ClimbSession?, gymName: String?) async {
// If we have an active session but no Live Activity, restart it
guard let activeSession = activeSession,
let gymName = gymName,
activeSession.status == .active
else {
return
}
// Check if we already have a running Live Activity
if currentActivity != nil {
print(" Live Activity already running")
return
}
print("🔄 Restarting Live Activity for existing session")
await startLiveActivity(for: activeSession, gymName: gymName)
}
/// Call this when a ClimbSession starts to begin a Live Activity
func startLiveActivity(for session: ClimbSession, gymName: String) async {
print("🔴 Starting Live Activity for gym: \(gymName)")
await endLiveActivity()
let attributes = SessionActivityAttributes(
gymName: gymName, startTime: session.startTime ?? session.date)
let initialContentState = SessionActivityAttributes.ContentState(
elapsed: 0,
totalAttempts: 0,
completedProblems: 0
)
do {
let activity = try Activity<SessionActivityAttributes>.request(
attributes: attributes,
contentState: initialContentState,
pushType: nil
)
self.currentActivity = activity
print("✅ Live Activity started successfully: \(activity.id)")
} catch {
print("❌ Failed to start live activity: \(error)")
print("Error details: \(error.localizedDescription)")
// Check specific error types
if error.localizedDescription.contains("authorization") {
print("Authorization error - check Live Activity permissions in Settings")
} else if error.localizedDescription.contains("content") {
print("Content error - check ActivityAttributes structure")
}
}
}
/// Call this to update the Live Activity with new session progress
func updateLiveActivity(elapsed: TimeInterval, totalAttempts: Int, completedProblems: Int) async
{
guard let currentActivity else {
print("⚠️ No current activity to update")
return
}
print(
"🔄 Updating Live Activity - Attempts: \(totalAttempts), Completed: \(completedProblems)"
)
let updatedContentState = SessionActivityAttributes.ContentState(
elapsed: elapsed,
totalAttempts: totalAttempts,
completedProblems: completedProblems
)
do {
await currentActivity.update(using: updatedContentState, alertConfiguration: nil)
print("✅ Live Activity updated successfully")
} catch {
print("❌ Failed to update live activity: \(error)")
}
}
/// Call this when a ClimbSession ends to end the Live Activity
func endLiveActivity() async {
guard let currentActivity else {
print(" No current activity to end")
return
}
print("🔴 Ending Live Activity: \(currentActivity.id)")
do {
await currentActivity.end(using: nil, dismissalPolicy: .immediate)
self.currentActivity = nil
print("✅ Live Activity ended successfully")
} catch {
print("❌ Failed to end live activity: \(error)")
self.currentActivity = nil
}
}
/// Check if Live Activities are available and authorized
func checkLiveActivityAvailability() -> String {
let authorizationInfo = ActivityAuthorizationInfo()
let status = authorizationInfo.areActivitiesEnabled
let message = """
Live Activity Status:
• Enabled: \(status)
• Authorization: \(authorizationInfo.areActivitiesEnabled ? "Granted" : "Denied/Unknown")
• Current Activity: \(currentActivity?.id.description ?? "None")
"""
print(message)
return message
}
/// Start periodic updates for Live Activity
func startPeriodicUpdates(for session: ClimbSession, totalAttempts: Int, completedProblems: Int)
{
guard currentActivity != nil else { return }
Task {
while currentActivity != nil {
let elapsed = Date().timeIntervalSince(session.startTime ?? session.date)
await updateLiveActivity(
elapsed: elapsed,
totalAttempts: totalAttempts,
completedProblems: completedProblems
)
// Wait 30 seconds before next update
try? await Task.sleep(nanoseconds: 30_000_000_000)
}
}
}
}