I have the following model class:
@Model
public final class FHIRObservation {
@Attribute(.allowsCloudEncryption) let hkID: UUID
@Attribute(.allowsCloudEncryption) let fhirID: String
@Attribute(.allowsCloudEncryption) var name: String
@Attribute(.allowsCloudEncryption) var status: String
@Attribute(.allowsCloudEncryption) var code: FHIRCodeableConcept
@Attribute(.allowsCloudEncryption) var value: FHIRLabValueType
@Attribute(.allowsCloudEncryption) var range: FHIRLabRange?
@Attribute(.allowsCloudEncryption) var lastUpdated: Date?
init(hkID: UUID, fhirID: String, name: String, status: String, code: FHIRCodeableConcept, value: FHIRLabValueType, range: FHIRLabRange? = nil, lastUpdated: Date? = nil) {
self.hkID = hkID
self.fhirID = fhirID
self.name = name
self.status = status
self.code = code
self.value = value
self.range = range
self.lastUpdated = lastUpdated
}
}
the following class UserData
:
import SwiftUI
import SwiftData
import os
@MainActor
public class UserData: ObservableObject {
let userDataLogger = Logger(
subsystem:
"com.cyrilzakka.Cardya.UserData",
category: "Model")
public static let shared = UserData()
public lazy var healthKitManager = HKManager(withModel: self)
func updateObservations(newObservations: [FHIRObservation], deletedObservations: [UUID]) async {
// TODO: Delete and add objects using Swift Data
}
}
and the following actor HKManager
:
public actor HKManager {
// MARK: - Properties
private lazy var isAuthorized = false
private weak var model: UserData?
...
@discardableResult
public func loadLabResults() async -> Bool {
guard isAvailable else {
hkLogger.debug("HealthKit is not available on this device.")
return false
}
hkLogger.debug("Loading lab results from HealthKit")
do {
let (samples, deletedSamples, newAnchor) = try await queryLabResults()
self.anchor = newAnchor
guard let samples = samples else {
hkLogger.debug("Lab results returned by HealthKit are empty.")
return false
}
// Cast from HKSample to HKClinicalRecord, extract HKFHIRResource and convert to ObservationProtocol
let fhirResources = try samples.compactMap { sample -> FHIRObservation? in
guard let clinicalRecord = sample as? HKClinicalRecord,
let fhirResource = clinicalRecord.fhirResource else {
return nil
}
let item = try self.displayObservationConvertible(for: fhirResource)
return FHIRObservation(hkID: sample.uuid, fhirID: fhirResource.identifier, name: clinicalRecord.displayName, status: item.labStatus, code: item.labCoding, value: item.labValue, range: item.labRange, lastUpdated: item.lastUpdated)
}
// Get IDs of observations to be deleted
var uuidsToDelete: [UUID] = []
if let deletedSamples = deletedSamples, !deletedSamples.isEmpty {
uuidsToDelete = deletedSamples.lazy.map { deletedObject -> UUID in
if let uuidString = deletedObject.metadata?[HKMetadataKeySyncIdentifier] as? String,
let uuid = UUID(uuidString: uuidString) {
return uuid
}
return deletedObject.uuid
}
}
// THIS CAUSES AN ERROR:
// await self.model?.updateObservations(newObservations: fhirResources, deletedObservations: uuidsToDelete)
return true
} catch {
hkLogger.error("An error occurred while querying for samples: (error.localizedDescription)")
return false
}
}
How do I go about saving the new FHIRObservations
to SwiftData? My current approach await self.model?.updateObservations(newObservations: fhirResources, deletedObservations: uuidsToDelete)
leads to a race condition according to Swift 6 since I’m trying to send fhiresources:
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
I’m also unclear as to how I’d update my data store with the newly fetched resources. Any help would be appreciated!