Swift's concurrency system provides built-in support for writing asynchronous and parallel code in a structured way. For a detailed overview of the Swift concurrency system, refer to the Swift Programming Language Concurrency topic.
While the considerations on this page broadly apply to using realm with Swift concurrency features, Realm Swift SDK version 10.39.0 adds support for using Realm with Swift Actors. You can use Realm isolated to a single actor or use Realm across actors.
Realm's actor support simplifies using Realm in a MainActor and background actor context, and supersedes much of the advice on this page regarding concurrency considerations. For more information, refer to Use Realm with Actors - Swift SDK.
Advertencias sobre la concurrencia de Realm
As you implement concurrency features in your app, consider this caveat about Realm's threading model and Swift concurrency threading behaviors.
Suspending Execution with Await
En cualquier lugar donde utilice la palabra clave Swift await Marca un posible punto de suspensión en la ejecución de tu código. Con Swift 5.7, una vez que tu código se suspende, el código subsiguiente podría no ejecutarse en el mismo hilo. Esto significa que, en cualquier momento que uses await en tu código, el código subsiguiente podría ejecutarse en un hilo diferente al del código anterior o posterior.
Esto es intrínsecamente incompatible con el paradigma de objetos activos de Realm. Los objetos activos, las colecciones y las instancias Realm están confinados a un hilo: es decir, sólo son válidos en el hilo en el que se crearon. En la práctica, esto significa que no puedes pasar instancias en vivo a otros hilos. Sin embargo, Realm ofrece varios mecanismos para el uso compartido de objetos entre hilos. Estos mecanismos suelen requerir que tu código realice algún manejo explícito para transferir datos de forma segura entre hilos.
Puede utilizar algunos de estos mecanismos, como objetos congelados o la ThreadSafeReference, para utilizar de forma segura objetos Realm e instancias entre hilos utilizando la palabra clave await. También puedes evitar problemas relacionados con la ejecución simultánea marcando cualquier código asíncrono de Realm con @MainActor para garantizar que tus aplicaciones siempre ejecuten este código en el hilo principal.
As a general rule, keep in mind that using Realm in an await context without incorporating threading protection may yield inconsistent behavior. Sometimes, the code may succeed. In other cases, it may throw an error related to writing on an incorrect thread.
Async/Await APIs
Many Realm Swift APIs that involve working with an Atlas App Services app or a synchronized realm are compatible with Swift's async/await syntax. For examples, check out:
Si tienes solicitudes de funcionalidades específicas relacionadas con las APIs asíncronas/await de Swift, consulta el Motor de opiniones MongoDB para Realm. El equipo de Realm Swift SDK planea continuar desarrollando funcionalidades relacionadas con la concurrencia basándose en la retroalimentación de la comunidad y en la evolución de la concurrencia de Swift.
Perform Background Writes
Un caso de uso comúnmente solicitado para el código asíncrono es realizar operaciones de guardar en segundo plano sin bloquear el hilo principal.
Realm has two APIs that allow for performing asynchronous writes:
La API writeAsync() permite realizar escrituras asincrónicas utilizando controladores de finalización Swift.
La API asyncWrite() permite realizar escrituras asincrónicas utilizando la sintaxis async/await de Swift.
Ambas APIs te permiten añadir, actualizar o borrar objetos en segundo plano sin utilizar objetos congelados o pasar una referencia segura para hilos.
With the writeAsync() API, waiting to obtain the write lock and committing a transaction occur in the background. The write block itself runs on the calling thread. This provides thread-safety without requiring you to manually handle frozen objects or passing references across threads.
Sin embargo, mientras se ejecuta el bloque de escritura, se bloquean nuevas transacciones en el hilo que realiza la llamada. Esto significa que una escritura grande con la API writeAsync() podría bloquear escrituras pequeñas y rápidas durante su ejecución.
The asyncWrite() API suspends the calling task while waiting for its turn to write rather than blocking the thread. In addition, the actual I/O to write data to disk is done by a background worker thread. For small writes, using this function on the main thread may block the main thread for less time than manually dispatching the write to a background thread.
For more information, including code examples, refer to: Perform a Background Write.
Tasks and TaskGroups
Swift concurrency provides APIs to manage Tasks and TaskGroups. The Swift concurrency documentation defines a task as a unit of work that can be run asynchronously as part of your program. Task allows you to specificially define a unit of asynchronous work. TaskGroup lets you define a collection of Tasks to execute as a unit under the parent TaskGroup.
Las tareas y los grupos de tareas permiten asignar el hilo a otras tareas importantes o cancelar una tarea de larga duración que podría bloquear otras operaciones. Para obtener estas ventajas, podrías considerar usar tareas y grupos de tareas para gestionar las escrituras en el dominio en segundo plano.
However, the thread-confined constraints described in Suspending Execution with Await above apply in the Task context. If your Task contains await points, subsequent code might run or resume on a different thread and violate Realm's thread confinement.
You must annotate functions that you run in a Task context with @MainActor to ensure code that accesses Realm only runs on the main thread. This negates some of the benefits of using Tasks, and may mean this is not a good design choice for apps that use Realm unless you are using Tasks solely for networking activities like managing users.
Actor Isolation
Tip
Use Realm with Swift Actors
The information in this section is applicable to Realm SDK versions earlier than 10.39.0. Starting in Realm Swift SDK version 10.39.0 and newer, the SDK supports using Realm with Swift Actors and related async functionality.
For more information, refer to Use Realm with Actors - Swift SDK.
Actor isolation provides the perception of confining Realm access to a dedicated actor, and therefore seems like a safe way to manage Realm access in an asynchronous context.
However, using Realm in a non-@MainActor async function is currently not supported.
En Swift 5.6, esto a menudo funcionaba por casualidad. La ejecución después de un await continuaría en cualquier hilo en el que se ejecutó lo que se esperaba. El uso de await Realm() en una función asíncrona haría que el código posterior se ejecute en el hilo principal hasta la siguiente llamada a una función aislada por un actor.
Swift 5.7 instead hops threads whenever changing actor isolation contexts. An unisolated async function always runs on a background thread instead.
Si tienes código que utiliza await Realm() y funciona en la versión 5.6, marcar la función como @MainActor hará que funcione con Swift 5.7. Funcionará como lo hizo - sin querer - en 5.6.
Errores relacionados con el código de concurrencia
La mayoría de las veces, el error que ves relacionado con el acceso a Realm a través de código concurrente es Realm accessed from incorrect thread. Esto se debe a los problemas de aislamiento de hilos que se describen en esta página.
Para evitar problemas relacionados con el threading en el código que utiliza funcionalidades de concurrencia de Swift:
Actualiza a una versión del SDK de Realm Swift que admita realms aislados por actor y utiliza esto como una alternativa a la gestión manual de hilos. Para obtener más información, consulta Usar Realm con Actores - Swift SDK.
No cambie los contextos de ejecución al acceder a un dominio. Si abre un dominio en el hilo principal para proporcionar datos a su interfaz de usuario, anote con
@MainActorlas funciones posteriores donde acceda al dominio de forma asíncrona para garantizar que siempre se ejecuten en el hilo principal. Recuerde queawaitmarca un punto de suspensión que podría cambiar a otro hilo.Apps that do not use actor-isolated realms can use the
writeAsyncAPI to perform a background write. This manages realm access in a thread-safe way without requiring you to write specialized code to do it yourself. This is a special API that outsources aspects of the write process - where it is safe to do so - to run in an async context. Unless you are writing to an actor-isolated realm, you do not use this method with Swift'sasync/awaitsyntax. Use this method synchronously in your code. Alternately, you can use theasyncWriteAPI with Swift'sasync/awaitsyntax when awaiting writes to asynchronous realms.If you want to explicitly write concurrency code that is not actor-isolated where accessing a realm is done in a thread-safe way, you can explicitly pass instances across threads where applicable to avoid threading-related crashes. This does require a good understanding of Realm's threading model, as well as being mindful of Swift concurrency threading behaviors.
Tipos enviables, no enviables y confinados a subprocesos
The Realm Swift SDK public API contains types that fall into three broad categories:
Sendable
Not Sendable and not thread confined
Thread-confined
You can share types that are not Sendable and not thread confined between threads, but you must synchronize them.
Los tipos confinados por subprocesos, a menos que estén congelados, se limitan a un contexto de aislamiento. No se pueden pasar entre estos contextos, ni siquiera con sincronización.
Sendable | Non-Sendable | Limitado a hilos |
|---|---|---|
CualquierBSON | RLMAppConfiguration | AnyRealmCollection |
AsyncOpen | RLMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RLMFindOptions | Lista |
RLMAPIKeyAuth | Transporte en red RLM | Map |
Aplicación RLM | RLMRequest | MutableSet |
RLMAsyncOpenTask | Respuesta de RLMR | Proyección |
RLMChangeStream | Configuración de RLMSync | RLMArray |
RLMCompensatingWriteInfo | RLMSyncTimeoutOptions | RLMChangeStream |
RLMCredentials | RLMDictionary | |
RLMDecimal128 | RLMDictionaryChange | |
RLMEmailPasswordAuth | RLMEmbeddedObject | |
RLMMaxKey | RLMLinkingObjects | |
RLMMinKey | RLMObject | |
RLMMongoClient | RLMPropertyChange | |
RLMMongoCollection | RLMRealm | |
RLMMongoDatabase | RLMResults | |
RLMObjectId | RLMSection | |
RLMObjectSchema | RLMSectionedResults | |
Notificación de progreso de RLM | RLMSectionedResultsChangeset | |
RLMProgressNotificationToken | Conjunto RLM | |
RLMProperty | RLMSyncSubscription | |
RLMPropertyDescriptor | RLMSyncSubscriptionSet | |
RLMProviderClient | RealmOptional | |
Cliente RLMPush | RealmProperty | |
RLMSchema | ||
RLMSortDescriptor | ||
Token de acción de error de sincronización RLM | ||
RLMSyncManager | ||
RLMSyncSession | ||
RLMThreadSafeReference | ||
RLMUpdateResult | ||
RLMUser | ||
RLMUserAPIKey | ||
RLMUserIdentity | ||
RLMUserProfile | ||
ThreadSafe |