import Foundation /// 崩溃文件持久化 enum LogStorage { private static let crashDir = "xuqm_bugcollect_crashes" private static let encoder: JSONEncoder = { let e = JSONEncoder() e.dateEncodingStrategy = .iso8601 return e }() /// 保存崩溃到磁盘 static func saveCrash(_ event: IssueEvent) { let dir = crashDirectory() try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) let filename = "\(Int(Date().timeIntervalSince1970))_\(event.fingerprint.prefix(8)).json" let url = dir.appendingPathComponent(filename) if let data = try? encoder.encode(event) { try? data.write(to: url) } } /// 加载待处理的崩溃 static func loadPendingCrashes() -> [IssueEvent] { let dir = crashDirectory() guard let files = try? FileManager.default.contentsOfDirectory( at: dir, includingPropertiesForKeys: nil ) else { return [] } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 return files .filter { $0.pathExtension == "json" } .compactMap { url -> IssueEvent? in guard let data = try? Data(contentsOf: url) else { return nil } return try? decoder.decode(IssueEvent.self, from: data) } } /// 清除已上传的崩溃 static func clearPendingCrashes() { let dir = crashDirectory() guard let files = try? FileManager.default.contentsOfDirectory( at: dir, includingPropertiesForKeys: nil ) else { return } for file in files { try? FileManager.default.removeItem(at: file) } } private static func crashDirectory() -> URL { let caches = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! return caches.appendingPathComponent(crashDir) } }