Skip to content

Instantly share code, notes, and snippets.

@benrudhart
Last active July 5, 2024 15:39
Show Gist options
  • Save benrudhart/d25518c25a702a85b008e2a5992881d0 to your computer and use it in GitHub Desktop.
Save benrudhart/d25518c25a702a85b008e2a5992881d0 to your computer and use it in GitHub Desktop.
AsyncModelActor.swift
import Foundation
import SwiftData
/// The `AsyncModelActor` protocol leverages SwiftData's `@ModelActor` to safely and concurrently perform operations (CRUD) on different threads.
///
/// ### Problem:
/// By default, all `ModelActor` instances execute tasks on the thread they were initialized on. This means that if a `@ModelActor` is initialized on the main thread
/// (e.g., in a SwiftUI View or ViewModel), all its operations will run on the main thread, potentially leading to performance issues.
///
/// ### Solution:
/// To address this, you can use `Task.detached { ... }` to move the initialization of the `ModelActor` to a background thread.
/// The `AsyncModelActor` protocol provides a convenience initializer to facilitate this process.
///
/// ### Example:
/// ```swift
/// @ModelActor
/// actor MyModelActor: AsyncModelActor {
/// func performDataOperation() {
/// // This is no longer executed on main thread
/// }
/// }
///
/// @MainActor
/// struct ContentView: View {
/// @Environment(\.modelContext) var context
///
/// var body: some View {
/// Text("Hello world")
/// .task(priority: .background, performActorOperation)
/// }
///
/// @Sendable
/// func performActorOperation() async {
/// let actor = await MyModelActor(modelContainer: context.container)
/// await actor.performDataOperation()
/// }
/// }
/// ```
public protocol AsyncModelActor: Sendable {
init(modelContainer: ModelContainer)
}
public extension AsyncModelActor {
/// Initializes the model actor on a background thread. Actors perform their operations on the thread they're initialized on!
init(modelContainer: ModelContainer, priority: TaskPriority?) async {
// detached Task is required, otherwise we cannot get off the main thread
let task = Task.detached(priority: priority) {
// swiftlint:disable:next explicit_init
Self.init(modelContainer: modelContainer) // Use the initializer offered by @ModelActor
}
self = await task.value
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment