Proper 1.0 release for iOS. Pending App Store submission.
This commit is contained in:
280
ios/OpenClimb/Views/LiveActivityDebugView.swift
Normal file
280
ios/OpenClimb/Views/LiveActivityDebugView.swift
Normal file
@@ -0,0 +1,280 @@
|
||||
//
|
||||
// LiveActivityDebugView.swift
|
||||
// OpenClimb
|
||||
//
|
||||
// Created by Assistant on 2025-09-15.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LiveActivityDebugView: View {
|
||||
@EnvironmentObject var dataManager: ClimbingDataManager
|
||||
@State private var debugOutput: String = ""
|
||||
@State private var isTestRunning = false
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
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("⚠️ 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) { _ in
|
||||
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("❌ iOS version does not support Live Activities (requires 16.1+)")
|
||||
}
|
||||
|
||||
// Check if we're on simulator
|
||||
#if targetEnvironment(simulator)
|
||||
appendDebugOutput("⚠️ 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("❌ 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("❌ 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)
|
||||
}
|
||||
Reference in New Issue
Block a user