diff --git a/StockMonitor/Models/Stock.swift b/StockMonitor/Models/Stock.swift index 800a542..c308d69 100644 --- a/StockMonitor/Models/Stock.swift +++ b/StockMonitor/Models/Stock.swift @@ -12,7 +12,7 @@ enum Market: String, Codable, CaseIterable { } } -struct Stock: Identifiable, Codable, Equatable { +struct Stock: Identifiable, Equatable { var id: String // 股票代码,如 "sh600000"、"usr_aapl"、"hk00700" var name: String var market: Market @@ -31,3 +31,19 @@ struct Stock: Identifiable, Codable, Equatable { return quote.change * shares } } + +// MARK: - Codable(容错:未知字段用默认值,避免新版本解码老数据时整条失败) +extension Stock: Codable { + enum CodingKeys: String, CodingKey { + case id, name, market, costPrice, holdingShares + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + id = try c.decode(String.self, forKey: .id) + name = try c.decode(String.self, forKey: .name) + market = try c.decode(Market.self, forKey: .market) + costPrice = try c.decodeIfPresent(Double.self, forKey: .costPrice) + holdingShares = try c.decodeIfPresent(Double.self, forKey: .holdingShares) + } +} diff --git a/StockMonitor/State/AppState.swift b/StockMonitor/State/AppState.swift index 6c90d25..7b3b329 100644 --- a/StockMonitor/State/AppState.swift +++ b/StockMonitor/State/AppState.swift @@ -1,6 +1,7 @@ import SwiftUI import Combine import AppKit +import os @MainActor final class AppState: ObservableObject { @@ -40,6 +41,8 @@ final class AppState: ObservableObject { } private func saveStocks(_ stocks: [Stock]) { + // 防止把空数组覆盖掉磁盘上有内容的文件 + if stocks.isEmpty, !Self.loadStocks().isEmpty { return } guard let data = try? JSONEncoder().encode(stocks) else { return } Self.backupIfNeeded() try? data.write(to: Self.stocksFileURL, options: .atomic) @@ -84,7 +87,8 @@ final class AppState: ObservableObject { init() { appLogger.info("AppState init start") logToFile("AppState init start") - self.stocks = Self.loadStocks() + // 用 _stocks 直接赋值,绕过 didSet,避免加载失败时把空数组覆盖写回磁盘 + _stocks = Published(wrappedValue: Self.loadStocks()) appLogger.info("AppState stocks loaded: \(self.stocks.count)") logToFile("AppState stocks loaded: \(self.stocks.count)") setupScheduler() diff --git a/StockMonitor/StockMonitorApp.swift b/StockMonitor/StockMonitorApp.swift index de755c1..353db02 100644 --- a/StockMonitor/StockMonitorApp.swift +++ b/StockMonitor/StockMonitorApp.swift @@ -1,6 +1,7 @@ import SwiftUI import ServiceManagement import AppKit +import os class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) {