You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")

错误原因

  1. 第一个错误:$timer是TimerStructManager的绑定对象,但TimerStructManager仅持有timerStructs数组属性,没有直接的exerciseTime成员。错误地将管理数组的对象当作单个TimerStruct实例访问属性。
  2. 第二个错误:$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:15:29