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.
Realm Concurrency Caveats
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:
The writeAsync() API allows for performing async writes using Swift completion handlers.
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.
However, while the write block itself is executed, this does block new transactions on the calling thread. This means that a large write using the writeAsync() API could block small, quick writes while it executes.
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.
Tasks and TaskGroups provide the ability to yield the thread to other important work or to cancel a long-running task that could be blocking other operations. To get these benefits, you might be tempted to use Tasks and TaskGroups to manage realm writes in the background.
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.
Do not change execution contexts when accessing a realm. If you open a realm on the main thread to provide data for your UI, annotate subsequent functions where you access the realm asynchronously with
@MainActorto ensure it always runs on the main thread. Remember thatawaitmarks a suspension point that could change to a different thread.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.
Sendable, Non-Sendable and Thread-Confined Types
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.
Thread-confined types, unless frozen, are confined to an isolation context. You cannot pass them between these contexts even with synchronization.
Sendable | Non-Sendable | Limitado a hilos |
|---|---|---|
AnyBSON | RLMAppConfiguration | AnyRealmCollection |
AsyncOpen | RLMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RLMFindOptions | Lista |
RLMAPIKeyAuth | RLMNetworkTransport | Map |
RLMApp | RLMRequest | MutableSet |
RLMAsyncOpenTask | RLMResponse | Proyección |
RLMChangeStream | RLMSyncConfiguration | RLMArray |
RLMCompensatingWriteInfo | RLMSyncTimeoutOptions | RLMChangeStream |
RLMCredentials | RLMDictionary | |
RLMDecimal128 | RLMDictionaryChange | |
RLMEmailPasswordAuth | RLMEmbeddedObject | |
RLMMaxKey | RLMLinkingObjects | |
RLMMinKey | RLMObject | |
RLMMongoClient | RLMPropertyChange | |
RLMMongoCollection | RLMRealm | |
RLMMongoDatabase | RLMResults | |
RLMObjectId | RLMSection | |
RLMObjectSchema | RLMSectionedResults | |
RLMProgressNotification | RLMSectionedResultsChangeset | |
RLMProgressNotificationToken | RLMSet | |
RLMProperty | RLMSyncSubscription | |
RLMPropertyDescriptor | RLMSyncSubscriptionSet | |
RLMProviderClient | RealmOptional | |
RLMPushClient | RealmProperty | |
RLMSchema | ||
RLMSortDescriptor | ||
RLMSyncErrorActionToken | ||
RLMSyncManager | ||
RLMSyncSession | ||
RLMThreadSafeReference | ||
RLMUpdateResult | ||
RLMUser | ||
RLMUserAPIKey | ||
RLMUserIdentity | ||
RLMUserProfile | ||
ThreadSafe |