SwiftUI运动App持久化报错:无法访问exerciseTime成员
运动App数据持久化集成错误排查
问题背景
开发运动App时,通过TimerStructManager管理记录运动时长的TimerStruct数据,集成到HomeView时遇到两个错误:
- 类型
ObservedObject<TimerStructManager>.Wrapper没有来自根类型TimerStructManager的动态成员exerciseTime - 无法将类型
Binding<Subject>转换为预期参数类型Int,报错行:let percent = Double($timer.exerciseTime/1500)
相关代码结构
TimerStruct 代码
import Foundation import SwiftUI struct TimerStruct: Codable, Identifiable { var id = UUID() var exerciseTime = 0.0 var isAlertpresented = false var countdownTimer = 300 var timerRunning = false var isPaused = false var isActive = false var isCompleted = false }
TimerStructManager 代码
import Foundation import SwiftUI class TimerStructManager: ObservableObject { @Published var timerStructs: [TimerStruct] = [] { didSet { save() } } let sampleTimerStructs: [TimerStruct] = [] init() { load() } func getArchiveURL() -> URL { let plistName = "timerStructs.plist" let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! return documentsDirectory.appendingPathComponent(plistName) } func save() { let archiveURL = getArchiveURL() let propertyListEncoder = PropertyListEncoder() let encodedTimerStructs = try? propertyListEncoder.encode(timerStructs) try? encodedTimerStructs?.write(to: archiveURL, options: .noFileProtection) } func load() { let archiveURL = getArchiveURL() let propertyListDecoder = PropertyListDecoder() var finalTimerStructs: [TimerStruct]! if let retrievedTimerStructData = try? Data(contentsOf: archiveURL), let decodedTimerStructs = try? propertyListDecoder.decode([TimerStruct].self, from: retrievedTimerStructData) { finalTimerStructs = decodedTimerStructs } else { finalTimerStructs = sampleTimerStructs } timerStructs = finalTimerStructs } }
HomeView 报错代码片段
let percent = Double($timer.exerciseTime/1500) Text("\(round(result: $timer.exerciseTime/60)) mins of exercise completed today")
错误原因
- 第一个错误:
$timer是TimerStructManager的绑定对象,但TimerStructManager仅持有timerStructs数组属性,没有直接的exerciseTime成员。错误地将管理数组的对象当作单个TimerStruct实例访问属性。 - 第二个错误:
$timer.exerciseTime返回的是Binding<Double>类型,不能直接参与数值计算,也无法传给需要Int类型参数的round函数。
修复方案
步骤1:定位目标TimerStruct实例
TimerStructManager管理的是[TimerStruct]数组,需先确定要操作的实例(比如当日的运动记录)。示例中先获取数组首个元素,实际可根据日期筛选:
// HomeView中声明ObservedObject @ObservedObject var manager = TimerStructManager() // 获取当日记录,示例简化取第一个元素 private var currentTimerStruct: TimerStruct? { manager.timerStructs.first }
步骤2:修正计算与视图代码
针对报错代码,改为直接访问数值而非绑定对象,并处理空值情况:
if let exerciseTime = currentTimerStruct?.exerciseTime { let percent = exerciseTime / 1500.0 Text("\(round(exerciseTime / 60)) mins of exercise completed today") } else { Text("0 mins of exercise completed today") }
额外优化:自动创建当日记录
避免空值问题,可在TimerStructManager中添加创建当日记录的方法:
// TimerStructManager中新增方法 func getOrCreateTodayTimerStruct() -> TimerStruct { // 实际需根据日期判断是否存在当日记录,此处简化处理 if let todayTimer = timerStructs.first { return todayTimer } else { let newTimer = TimerStruct() timerStructs.append(newTimer) return newTimer } }
在HomeView中调用:
private var currentTimerStruct: TimerStruct { manager.getOrCreateTodayTimerStruct() }
绑定的正确用法
若需修改exerciseTime,才需要使用绑定,比如向子视图传递:
TimerView(exerciseTime: $manager.timerStructs[0].exerciseTime)
内容的提问来源于stack exchange,提问作者Zudoturiku
相关产品推荐
相关产品推荐

