Files
Ascently/ios/OpenClimb/Views/LiveActivityDebugView.swift
Atridad Lahiji 4bbd422c09
All checks were successful
OpenClimb Docker Deploy / build-and-push (push) Successful in 2m28s
Added a proper set of Unit Tests for each sub-project
2025-10-03 20:55:04 -06:00

279 lines
10 KiB
Swift

//
// LiveActivityDebugView.swift
import SwiftUI
struct LiveActivityDebugView: View {
@EnvironmentObject var dataManager: ClimbingDataManager
@State private var debugOutput: String = ""
@State private var isTestRunning = false
var body: some View {
NavigationStack {
VStack(alignment: .leading, spacing: 20) {
// Header
VStack(alignment: .leading, spacing: 8) {
Text("Live Activity Debug")
.font(.title)
.fontWeight(.bold)
Text("Test and debug Live Activities for climbing sessions")
.font(.subheadline)
.foregroundColor(.secondary)
}
// Status Section
GroupBox("Current Status") {
VStack(alignment: .leading, spacing: 12) {
HStack {
Image(systemName: "circle.fill")
.foregroundColor(dataManager.activeSession != nil ? .green : .red)
Text(
"Active Session: \(dataManager.activeSession != nil ? "Yes" : "No")"
)
}
HStack {
Image(systemName: "building.2")
Text("Total Gyms: \(dataManager.gyms.count)")
}
if let activeSession = dataManager.activeSession,
let gym = dataManager.gym(withId: activeSession.gymId)
{
HStack {
Image(systemName: "location")
Text("Current Gym: \(gym.name)")
}
}
}
}
// Test Buttons
GroupBox("Live Activity Tests") {
VStack(spacing: 16) {
Button(action: checkStatus) {
HStack {
Image(systemName: "checkmark.circle")
Text("Check Live Activity Status")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.disabled(isTestRunning)
Button(action: testLiveActivity) {
HStack {
Image(systemName: isTestRunning ? "hourglass" : "play.circle")
Text(
isTestRunning
? "Running Test..." : "Run Full Live Activity Test")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.disabled(isTestRunning || dataManager.gyms.isEmpty)
Button(action: forceLiveActivityUpdate) {
HStack {
Image(systemName: "arrow.clockwise")
Text("Force Live Activity Update")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.disabled(dataManager.activeSession == nil)
if dataManager.gyms.isEmpty {
Text("WARNING: Add at least one gym to test Live Activities")
.font(.caption)
.foregroundColor(.orange)
}
if dataManager.activeSession != nil {
Button(action: endCurrentSession) {
HStack {
Image(systemName: "stop.circle")
Text("End Current Session")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.disabled(isTestRunning)
}
}
}
// Debug Output
GroupBox("Debug Output") {
ScrollView {
ScrollViewReader { proxy in
VStack(alignment: .leading, spacing: 4) {
if debugOutput.isEmpty {
Text("No debug output yet. Run a test to see details.")
.foregroundColor(.secondary)
.italic()
} else {
Text(debugOutput)
.font(.system(.caption, design: .monospaced))
.textSelection(.enabled)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.id("bottom")
.onChange(of: debugOutput) {
withAnimation {
proxy.scrollTo("bottom", anchor: .bottom)
}
}
}
}
.frame(maxHeight: 200)
.background(Color(UIColor.systemGray6))
.cornerRadius(8)
}
// Clear button
HStack {
Spacer()
Button("Clear Output") {
debugOutput = ""
}
.buttonStyle(.bordered)
}
Spacer()
}
.padding()
}
.navigationTitle("Live Activity Debug")
.navigationBarTitleDisplayMode(.inline)
}
private func appendDebugOutput(_ message: String) {
let timestamp = DateFormatter.timeFormatter.string(from: Date())
let newLine = "[\(timestamp)] \(message)"
DispatchQueue.main.async {
if debugOutput.isEmpty {
debugOutput = newLine
} else {
debugOutput += "\n" + newLine
}
}
}
private func checkStatus() {
appendDebugOutput("Checking Live Activity status...")
let status = LiveActivityManager.shared.checkLiveActivityAvailability()
appendDebugOutput("Status: \(status)")
// Check iOS version
if #available(iOS 16.1, *) {
appendDebugOutput("iOS version supports Live Activities")
} else {
appendDebugOutput(
"ERROR: iOS version does not support Live Activities (requires 16.1+)")
}
// Check if we're on simulator
#if targetEnvironment(simulator)
appendDebugOutput(
"WARNING: Running on Simulator - Live Activities have limited functionality")
#else
appendDebugOutput("Running on device - Live Activities should work fully")
#endif
}
private func testLiveActivity() {
guard !dataManager.gyms.isEmpty else {
appendDebugOutput("ERROR: No gyms available for testing")
return
}
isTestRunning = true
appendDebugOutput("🧪 Starting Live Activity test...")
Task {
defer {
DispatchQueue.main.async {
isTestRunning = false
}
}
// Test with first gym
let testGym = dataManager.gyms[0]
appendDebugOutput("Using gym: \(testGym.name)")
// Create test session
let testSession = ClimbSession(
gymId: testGym.id, notes: "Test session for Live Activity")
appendDebugOutput("Created test session")
// Start Live Activity
await LiveActivityManager.shared.startLiveActivity(
for: testSession, gymName: testGym.name)
appendDebugOutput("Live Activity start request sent")
// Wait and update
try? await Task.sleep(nanoseconds: 3_000_000_000) // 3 seconds
appendDebugOutput("Updating Live Activity with test data...")
await LiveActivityManager.shared.updateLiveActivity(
elapsed: 180,
totalAttempts: 8,
completedProblems: 2
)
// Another update
try? await Task.sleep(nanoseconds: 3_000_000_000) // 3 seconds
appendDebugOutput("Second update...")
await LiveActivityManager.shared.updateLiveActivity(
elapsed: 360,
totalAttempts: 15,
completedProblems: 4
)
// End after delay
try? await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds
appendDebugOutput("Ending Live Activity...")
await LiveActivityManager.shared.endLiveActivity()
appendDebugOutput("Live Activity test completed!")
}
}
private func endCurrentSession() {
guard let activeSession = dataManager.activeSession else {
appendDebugOutput("ERROR: No active session to end")
return
}
appendDebugOutput("Ending current session: \(activeSession.id)")
dataManager.endSession(activeSession.id)
appendDebugOutput("Session ended")
}
private func forceLiveActivityUpdate() {
appendDebugOutput("Forcing Live Activity update...")
dataManager.forceLiveActivityUpdate()
appendDebugOutput("Live Activity update sent")
}
}
extension DateFormatter {
static let timeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}()
}
#Preview {
LiveActivityDebugView()
.environmentObject(ClimbingDataManager.preview)
}