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

如何从Actor更新SwiftData存储?解决数据竞争问题

问题

如何将新的FHIRObservation保存到SwiftData中?我当前使用await self.model?.updateObservations(newObservations: fhirResources, deletedObservations: uuidsToDelete)的方式在Swift 6中引发了数据竞争问题,错误提示为:

Sending 'self'-isolated 'fhirResources' to main actor-isolated instance method 'updateObservations(newObservations:deletedObservations:)' risks causing data races between main actor-isolated and 'self'-isolated uses

同时我也不清楚如何用新获取的资源更新数据存储。我提供了FHIRObservation模型类、@MainActor修饰的UserData类以及HKManager Actor的代码。

更新1

我创建了与FHIRObservation属性相同且遵循Sendable协议的FHIRObservationWrapper,似乎解决了问题,这种方式是否可行?


解答

你的FHIRObservationWrapper方案完全可行,核心原因是Swift 6强化了并发安全检查:

  • 被@MainActor标记的updateObservations属于主线程隔离域,而你传入的fhirResources来自当前非主线程隔离域。如果FHIRObservation本身不遵循Sendable,Swift无法保证它跨隔离域传递时的线程安全性,因此抛出数据竞争警告。
  • 实现遵循Sendable的包装类,相当于明确告知Swift该类型是线程安全的,可安全跨隔离域传递,直接解决了警告问题。

SwiftData更新存储的具体实现建议

  1. 保证数据模型的并发合规性

    • 如果FHIRObservation是你自己定义的SwiftData模型,直接让它遵循Sendable更简洁:
      @Model
      final class FHIRObservation: Sendable {
          var id: UUID
          var fhirId: String
          var value: Double
          var date: Date
          // 其他属性
      
          init(id: UUID, fhirId: String, value: Double, date: Date) {
              self.id = id
              self.fhirId = fhirId
              self.value = value
              self.date = date
          }
      }
      
      标记为final的@Model类默认隐式遵循Sendable,这样可以直接传递原类型数组给主线程方法,无需额外包装。
    • 如果FHIRObservation是第三方库类无法修改,用FHIRObservationWrapper做中间层是合理选择,在包装类中完成数据转换,再传递安全的包装对象。
  2. 规范跨Actor数据流转
    在后台HKManager Actor中完成数据转换,再传递给主线程的UserData:

    // HKManager Actor内部代码
    let wrappedObservations = fhirResources.map { FHIRObservationWrapper(from: $0) }
    await userData.updateObservations(newObservations: wrappedObservations, deletedObservations: uuidsToDelete)
    
  3. 实现SwiftData的增删更新逻辑
    在@MainActor修饰的updateObservations方法中处理数据同步:

    @MainActor
    func updateObservations(newObservations: [FHIRObservationWrapper], deletedObservations: [UUID]) {
        // 1. 删除指定UUID的记录
        for uuid in deletedObservations {
            guard let observation = try? modelContext.fetch(FetchDescriptor<FHIRObservation>(predicate: #Predicate { $0.id == uuid })).first else {
                continue
            }
            modelContext.delete(observation)
        }
        // 2. 新增/更新记录
        for wrapped in newObservations {
            // 按唯一标识(比如fhirId)查找现有记录
            let existing = try? modelContext.fetch(FetchDescriptor<FHIRObservation>(predicate: #Predicate { $0.fhirId == wrapped.fhirId })).first
            if let existing {
                // 更新现有记录属性
                existing.value = wrapped.value
                existing.date = wrapped.date
                // 其他属性更新
            } else {
                // 创建新记录并插入上下文
                let observation = FHIRObservation(from: wrapped)
                modelContext.insert(observation)
            }
        }
        // 保存上下文
        do {
            try modelContext.save()
        } catch {
            print("SwiftData保存失败: \(error.localizedDescription)")
        }
    }
    

内容的提问来源于stack exchange,提问作者cyril

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 14:35:04