Swift中重启App后如何维护递增ID计数器的连续值?
解决Swift结构体实例重启后递增ID不接续的问题
你当前用静态序列生成ID的方式只在App运行时有效,因为序列是内存中的临时状态,App重启后会完全重置。要实现跨启动的连续ID,核心是持久化记录最后使用的ID,同时要和已保存的交易数据做同步(避免意外情况导致持久化ID和实际交易最大ID不一致)。
最佳实践方案
既然你的Transactions类已经负责交易数据的加载和保存,把ID的管理逻辑放在这里是最合理的——既可以利用已加载的交易数据校验ID合法性,又能统一处理持久化逻辑。
实现步骤
- 用
UserDefaults存储最后使用的Buy/Sell交易ID(这类轻量配置数据用UserDefaults足够,无需额外文件) - 初始化
Transactions时,先加载已保存的交易,再对比UserDefaults中存储的ID和交易列表中的最大ID,取较大值作为下一个ID的起始值 - 提供专门的ID生成方法,每次生成后立即更新
UserDefaults - 修改交易结构体的初始化逻辑,改为外部传入ID,去掉结构体内部的序列依赖
修改后的完整代码
1. 更新Transactions类
class Transactions: ObservableObject { @Published var buyTransactions = [BuyTransaction]() { didSet { writeFile() } } @Published var sellTransactions = [SellTransaction]() { didSet { writeFile() } } // 用于持久化ID的UserDefaults键 private let lastBuyIDKey = "LastBuyTransactionID" private let lastSellIDKey = "LastSellTransactionID" // 存储下一个要使用的ID private var nextBuyID: Int private var nextSellID: Int init() { // 先加载已保存的交易数据 readFile() // 从UserDefaults读取上次保存的ID,默认值为0 let savedLastBuyID = UserDefaults.standard.integer(forKey: lastBuyIDKey) let savedLastSellID = UserDefaults.standard.integer(forKey: lastSellIDKey) // 取已存在交易的最大ID和持久化ID的较大值,加1作为下一个ID的起始 let maxExistingBuyID = buyTransactions.map { $0.id }.max() ?? 0 nextBuyID = max(savedLastBuyID, maxExistingBuyID) + 1 let maxExistingSellID = sellTransactions.map { $0.id }.max() ?? 0 nextSellID = max(savedLastSellID, maxExistingSellID) + 1 } // 生成新的Buy交易ID并持久化 func generateNextBuyID() -> Int { let id = nextBuyID nextBuyID += 1 UserDefaults.standard.set(nextBuyID - 1, forKey: lastBuyIDKey) return id } // 生成新的Sell交易ID并持久化 func generateNextSellID() -> Int { let id = nextSellID nextSellID += 1 UserDefaults.standard.set(nextSellID - 1, forKey: lastSellIDKey) return id } // 保留你原有的文件读写逻辑 private func readFile() { // 原有逻辑:从文件加载buyTransactions和sellTransactions } private func writeFile() { // 原有逻辑:将buyTransactions和sellTransactions写入文件 } }
2. 简化交易结构体
struct BuyTransaction: Identifiable, Codable { let id: Int let dateTime: Date let someOtherValue: String // 改为接受外部传入的ID,去掉内部的序列依赖 init( id: Int, dateTime: Date, someOtherValue: String ) { self.id = id self.dateTime = dateTime self.someOtherValue = someOtherValue } } struct SellTransaction: Identifiable, Codable { let id: Int let dateTime: Date let someOtherValue: String let andMoreValues: String init( id: Int, dateTime: Date, someOtherValue: String, andMoreValues: String ) { self.id = id self.dateTime = dateTime self.someOtherValue = someOtherValue self.andMoreValues = andMoreValues } }
3. 使用示例
创建新交易时,通过Transactions实例生成ID:
// 假设你已经初始化了Transactions实例 let transactions = Transactions() // 创建新的买入交易 let newBuyTx = BuyTransaction( id: transactions.generateNextBuyID(), dateTime: Date(), someOtherValue: "测试买入" ) transactions.buyTransactions.append(newBuyTx) // 创建新的卖出交易 let newSellTx = SellTransaction( id: transactions.generateNextSellID(), dateTime: Date(), someOtherValue: "测试卖出", andMoreValues: "附加数据" ) transactions.sellTransactions.append(newSellTx)
方案优势
- 统一管理:ID生成逻辑集中在
Transactions类,避免多个结构体各自维护状态 - 数据安全:同时校验持久化ID和已保存交易的最大ID,防止App崩溃等意外情况导致的ID重复
- 轻量高效:用
UserDefaults存储ID,无需额外文件或数据库操作 - 职责清晰:交易结构体只负责存储数据,ID由外部管理,符合单一职责原则
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

