Last active
October 14, 2024 10:04
-
-
Save benrudhart/d25518c25a702a85b008e2a5992881d0 to your computer and use it in GitHub Desktop.
AsyncModelActor.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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