SyncDiagnosticsView.swift (3627B)
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("Last Error Code", syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None") 19 row("Last Error Description", syncMonitor.lastCloudKitErrorDescription ?? "None") 20 row( 21 "Last Success", 22 syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None" 23 ) 24 } 25 26 Section("Recent Events") { 27 if syncMonitor.recentDiagnostics.isEmpty { 28 Text("No events captured yet.") 29 .foregroundStyle(.secondary) 30 } else { 31 ForEach(syncMonitor.recentDiagnostics.reversed()) { entry in 32 VStack(alignment: .leading, spacing: 4) { 33 Text( 34 "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]" 35 ) 36 .font(.caption.monospaced()) 37 .foregroundStyle(.secondary) 38 39 Text(entry.message) 40 .font(.caption.monospaced()) 41 .textSelection(.enabled) 42 } 43 .padding(.vertical, 2) 44 } 45 } 46 } 47 } 48 .textSelection(.enabled) 49 .toolbar { 50 ToolbarItem { 51 Button("Copy") { 52 let pasteboard = NSPasteboard.general 53 pasteboard.clearContents() 54 pasteboard.setString(diagnosticDump, forType: .string) 55 } 56 } 57 } 58 } 59 60 @ViewBuilder 61 private func row(_ title: String, _ value: String) -> some View { 62 VStack(alignment: .leading, spacing: 4) { 63 Text(title) 64 .font(.caption) 65 .foregroundStyle(.secondary) 66 Text(value) 67 .font(.body.monospaced()) 68 .textSelection(.enabled) 69 } 70 .padding(.vertical, 2) 71 } 72 73 private var diagnosticDump: String { 74 var lines: [String] = [] 75 lines.append("Transient Banner: \(syncMonitor.transientErrorMessage ?? "None")") 76 lines.append("Last Error Domain: \(syncMonitor.lastCloudKitErrorDomain ?? "None")") 77 lines.append("Last Error Code: \(syncMonitor.lastCloudKitErrorCode.map(String.init) ?? "None")") 78 lines.append("Last Error Description: \(syncMonitor.lastCloudKitErrorDescription ?? "None")") 79 lines.append( 80 "Last Success: \(syncMonitor.lastSuccessfulSyncDate.map(Self.timestampFormatter.string(from:)) ?? "None")" 81 ) 82 lines.append("") 83 lines.append("Recent Events:") 84 for entry in syncMonitor.recentDiagnostics { 85 lines.append( 86 "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)" 87 ) 88 } 89 return lines.joined(separator: "\n") 90 } 91 }