SwiftUI秒表应用关闭重启后状态持久化问题求助
SwiftUI秒表应用持久化问题
- 启动活动计时后,关闭再重启应用,秒表无法继续计时,预期后台仍持续记录时长
- 重启应用后,当前活动及已归档活动的记录时间全部重置为0,需要持久化两类活动的时间数据
附相关代码:
struct Activity: Identifiable, Codable, Equatable { ... var startTime: Date? // 记录活动开始时间 ... init(from decoder: Decoder) throws { ... let startTimeKey = "startTime\(id.uuidString)" startTime = UserDefaults.standard.object(forKey: startTimeKey) as? Date // 从UserDefaults加载startTime } func encode(to encoder: Encoder) throws { ... let startTimeKey = "startTime\(id.uuidString)" UserDefaults.standard.set(startTime, forKey: startTimeKey) // 将startTime保存到UserDefaults } ... } class ViewModel: ObservableObject { ... func startActivity(_ index: Int) { // 记录活动开始时间 activities[index].startTime = Date() activities[index].isPlaying = true saveActivities() } func stopActivity(_ index: Int) { // 清空活动开始时间 activities[index].startTime = nil activities[index].isPlaying = false saveActivities() } func loadActivities() { activities = store.load() // 处理应用关闭时正在运行的活动,计算已流逝时间 for index in activities.indices where activities[index].isPlaying { guard let startTime = activities[index].startTime else { continue } let timeElapsed = Date().timeIntervalSince(startTime) activities[index].currentTime += timeElapsed activities[index].startTime = Date() // 重置startTime为当前时间 } saveActivities() } ... }
问题分析与修复方案
核心问题1:startTime的持久化逻辑错误
你在Codable的init和encode方法里直接操作UserDefaults,但Codable协议的方法不应该用来做额外的存储操作——当编码/解码Activity数组时,每个Activity都会读写UserDefaults,但这会导致:
- 解码时,
id还没完成初始化就被用来生成startTimeKey,此时id可能是默认值,导致读取错误的键 - 编码时,
UserDefaults的操作和数组的持久化不同步,容易出现数据不一致
修复:把startTime纳入Codable编码,不再单独存UserDefaults
修改Activity结构,让startTime成为Codable的一部分,去掉自定义的init(from:)和encode(to:):
struct Activity: Identifiable, Codable, Equatable { let id = UUID() var isPlaying: Bool var currentTime: TimeInterval var startTime: Date? // 该属性会被自动编码/解码 // 移除自定义的init(from:)和encode(to:),使用默认实现 }
核心问题2:loadActivities的时间计算逻辑错误
当前代码在加载活动时,累加currentTime后直接重置startTime并保存,但如果store.load()读取的startTime是旧值(因为之前没正确持久化),就会导致时间计算错误。另外,归档活动的currentTime没有被正确持久化——需要确保currentTime在每次停止活动或应用退出时都被正确保存。
修复:修正加载时的时间计算逻辑
func loadActivities() { activities = store.load() let now = Date() for index in activities.indices where activities[index].isPlaying { guard let startTime = activities[index].startTime else { // 若startTime丢失,标记活动为停止状态 activities[index].isPlaying = false continue } // 计算从上次启动到现在的流逝时间,累加到currentTime let elapsed = now.timeIntervalSince(startTime) activities[index].currentTime += elapsed // 更新startTime为当前时间,保证后续计时连续 activities[index].startTime = now } saveActivities() }
核心问题3:应用退出时未处理正在运行的活动
当应用被强制关闭时,saveActivities()可能没被调用,导致startTime和currentTime的最新状态丢失。需要在应用进入后台时自动保存状态。
修复:监听应用生命周期事件,后台时保存状态
在ViewModel中添加生命周期监听:
class ViewModel: ObservableObject { private var cancellables = Set<AnyCancellable>() init() { // 监听应用进入后台事件 NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification) .sink { [weak self] _ in self?.saveActivities() } .store(in: &cancellables) } // ... 其他原有代码 ... }
额外建议:确保store的持久化逻辑正确
你的store.load()和saveActivities()需要正确处理Activity数组的编码/解码,以下是基于UserDefaults的实现示例:
// 保存活动数组 func saveActivities() { guard let data = try? JSONEncoder().encode(activities) else { return } UserDefaults.standard.set(data, forKey: "SavedActivities") } // 加载活动数组 func loadActivities() { if let data = UserDefaults.standard.data(forKey: "SavedActivities"), let savedActivities = try? JSONDecoder().decode([Activity].self, from: data) { activities = savedActivities } // ... 后续时间计算逻辑 ... }
内容的提问来源于stack exchange,提问作者Nenharmha
相关产品推荐
相关产品推荐

