listless

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

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 }