I'm working with SwiftData and SwiftUI and it's not clear to me if it is good practice to have a @ModelActor directly populate a SwiftUI view. For example when having to combine manual lab results and clinial results from HealthKit. The Clinical lab results are an async operation:
@ModelActor
actor LabResultsManager {
func fetchLabResultsWithHealthKit() async throws -> [LabResultDto] {
let manualEntries = try modelContext.fetch(FetchDescriptor<LabResult>())
let clinicalLabs = (try? await HealthKitService.getLabResults()) ?? []
return (manualEntries + clinicalLabs).sorted {
$0.date > $1.date
}.map {
return LabResultDto(from: $0)
}
}
}
struct ContentView: View {
@State private var labResults: [LabResultDto] = []
var body: some View {
List(labResults, id: \.id) { result in
VStack(alignment: .leading) {
Text(result.testName)
Text(result.date, style: .date)
}
}
.task {
do {
let labManager = LabResultsManager()
labResults = try await labManager.fetchLabResultsWithHealthKit()
} catch {
// Handle error
}
}
}
}
EDIT: I have a few views that would want to use these labResults so I need an implementation that can be reused. Having to fetch and combine in each view will not be good practice. Can I pass a modelContext to a viewModel?
It's unclear what LabResultDto
in the code snippet is. If it is a Sendable
type that wraps the data in a SwiftData model, that's fine; if it is a SwiftData model type, because a SwiftData model object is not Sendable
, you won't want to pass it across actors – Otherwise, Swift 6 compiler will give you an error. For more information about this topic, see the discussion here.
When using SwiftData + SwiftUI, I typically use @Query
to create a result set for a view. Under the hood (of @Query
), the query controller should be able to detect the changes you made from within a model actor, and trigger a SwiftUI update. Fore more information about observing SwiftData changes, see this WWDC25 video.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.