listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

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 }