SyncDiagnosticsView.swift (3684B)
1 import SwiftUI 2 3 struct SyncDiagnosticsView: View { 4 @ObservedObject var syncMonitor: CloudKitSyncMonitor 5 6 private static let timestampFormatter: DateFormatter = { 7 let formatter = DateFormatter() 8 formatter.dateStyle = .none 9 formatter.timeStyle = .medium 10 return formatter 11 }() 12 13 var body: some View { 14 List { 15 Section("Status") { 16 row("Transient Banner", syncMonitor.transientErrorMessage ?? "None") 17 row("Last Error Domain", syncMonitor.lastCloudKitErrorDomain ?? "None") 18 row( 19 "Last Error Code", 20 syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None" 21 ) 22 row("Last Error Description", syncMonitor.lastCloudKitErrorDescription ?? "None") 23 row( 24 "Last Success", 25 syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) 26 ?? "None" 27 ) 28 } 29 30 Section("Recent Events") { 31 if syncMonitor.recentDiagnostics.isEmpty { 32 Text("No events captured yet.") 33 .foregroundStyle(.secondary) 34 } else { 35 ForEach(syncMonitor.recentDiagnostics.reversed()) { entry in 36 VStack(alignment: .leading, spacing: 4) { 37 Text( 38 "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]" 39 ) 40 .font(.caption.monospaced()) 41 .foregroundStyle(.secondary) 42 43 Text(entry.message) 44 .font(.caption.monospaced()) 45 .textSelection(.enabled) 46 } 47 .padding(.vertical, 2) 48 } 49 } 50 } 51 } 52 .navigationTitle("iCloud Diagnostics") 53 .navigationBarTitleDisplayMode(.inline) 54 .toolbar { 55 ToolbarItem(placement: .topBarTrailing) { 56 Button("Copy") { 57 UIPasteboard.general.string = diagnosticDump 58 } 59 } 60 } 61 } 62 63 @ViewBuilder 64 private func row(_ title: String, _ value: String) -> some View { 65 VStack(alignment: .leading, spacing: 4) { 66 Text(title) 67 .font(.caption) 68 .foregroundStyle(.secondary) 69 Text(value) 70 .font(.body.monospaced()) 71 .textSelection(.enabled) 72 } 73 .padding(.vertical, 2) 74 } 75 76 private var diagnosticDump: String { 77 var lines: [String] = [] 78 lines.append("Transient Banner: \(syncMonitor.transientErrorMessage ?? "None")") 79 lines.append("Last Error Domain: \(syncMonitor.lastCloudKitErrorDomain ?? "None")") 80 lines.append("Last Error Code: \(syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None")") 81 lines.append("Last Error Description: \(syncMonitor.lastCloudKitErrorDescription ?? "None")") 82 lines.append( 83 "Last Success: \(syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None")" 84 ) 85 lines.append("") 86 lines.append("Recent Events:") 87 for entry in syncMonitor.recentDiagnostics { 88 lines.append( 89 "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)" 90 ) 91 } 92 return lines.joined(separator: "\n") 93 } 94 }