mirror of
https://github.com/VGEAREN/Stockbar.git
synced 2026-10-06 14:33:11 +08:00
Fix config data loss on startup and Codable forward-compatibility
- AppState.init: use _stocks = Published(wrappedValue:) to bypass didSet, preventing empty array from overwriting disk on decode failure - AppState.saveStocks: guard against overwriting non-empty file with [] - Stock: explicit Codable extension using decodeIfPresent for optional fields, so missing fields in old JSON use defaults instead of failing the whole decode Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3227b4fcda
commit
a119f89624
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import SwiftUI
|
||||
import ServiceManagement
|
||||
import AppKit
|
||||
import os
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
|
||||
Reference in New Issue
Block a user