Docs Menu
Docs Home
/ /
Sincronizar datos

Escribir datos en un dominio sincronizado - Swift SDK

Al escribir datos en un dominio sincronizado mediante Sincronización Flexible, se pueden usar las mismas API que al escribir en un dominio local. Sin embargo, existen algunas diferencias de comportamiento que se deben tener en cuenta al desarrollar la aplicación.

Cuando escribe en un reino sincronizado, sus operaciones de escritura deben coincidir con ambos puntos siguientes:

  • La consulta de suscripción de sincronización.
    • Si su operación de escritura no coincide con la consulta en la suscripción, la escritura se revierte con un error de escritura compensatorio no fatal (ErrorCompensatingWrite).

  • Los permisos en su aplicación App Services.
    • Si intenta escribir datos que no coinciden con la expresión de permisos, la escritura se revierte con un error de permiso denegado (no fatal). En el cliente, esto se muestra como un error (ErrorCompensatingWrite). En el servidor, puede ver más detalles sobre cómo se denegó la escritura mediante un filtro de escritura en el rol.

    • Para obtener más información sobre cómo configurar permisos para su aplicación, consulte Permisos basados ​​en roles y la Guía de permisos de sincronización de dispositivos en la documentación de App Services.

Advertencia

La sincronización multiproceso no es compatible

Actualmente, Device Sync no permite abrir ni escribir en un dominio sincronizado desde más de un proceso. Para obtener más información, incluidas las alternativas sugeridas, consulta: No escribir en un dominio sincronizado en una extensión de aplicación.

Los datos que puede escribir en un reino sincronizado son la intersección de su configuración de sincronización de dispositivo, sus permisos y la consulta de suscripción de sincronización flexible que utiliza cuando abre el reino.

Los ejemplos de esta página utilizan las siguientes configuraciones y modelos:

La sincronización del dispositivo está configurada con los siguientes campos consultables:

  • _id (siempre incluido)

  • complexity

  • ownerId

La aplicación Servicios de aplicaciones tiene permisos configurados para permitir a los usuarios leer y escribir solo sus propios datos:

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": { "ownerId": "%%user.id" },
"write": { "ownerId": "%%user.id" }
},
"read": true,
"write": true
}

Los ejemplos de esta página utilizan el siguiente modelo de objetos:

class Item: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var ownerId: String
@Persisted var itemName: String
@Persisted var complexity: Int
}

Al utilizar ese modelo de objeto, la configuración del reino sincronizado sincroniza los objetos que coinciden con la consulta de suscripción donde el valor de la propiedad complexity es menor o igual a 4:

let app = App(id: YOUR_APP_ID_HERE)
do {
let user = try await app.login(credentials: Credentials.anonymous)
do {
var flexSyncConfig = user.flexibleSyncConfiguration()
flexSyncConfig.objectTypes = [Item.self]
let realm = try await Realm(configuration: flexSyncConfig)
let subscriptions = realm.subscriptions
try await subscriptions.update {
subscriptions.append(
QuerySubscription<Item>(name: "simple-items") {
$0.complexity <= 4
})
}
print("Successfully opened realm: \(realm)")
} catch {
print("Failed to open realm: \(error.localizedDescription)")
// handle error
}
} catch {
fatalError("Login failed: \(error.localizedDescription)")
}

La consulta de suscripción combinada con los permisos significa que el reino sincronizado solo sincroniza objetos donde:

  • El ownerId coincide con el user.id del usuario que inició sesión (de los permisos)

  • El valor de la propiedad complexity es menor o igual a 4 (de la consulta de suscripción)

Cualquier objeto en la colección Atlas donde ownerId no coincida con user.id del usuario que inició sesión, o el valor de la propiedad complexity sea mayor que 4, no se puede sincronizar con este reino.

Las escrituras en los dominios de sincronización flexible pueden clasificarse en dos categorías:

  • Escrituras correctas: El objeto escrito coincide con la suscripción de consulta y los permisos del usuario. El objeto se escribe correctamente en el dominio y se sincroniza correctamente con el backend de App Services y otros dispositivos.

  • Escrituras compensatorias: cuando el objeto escrito no coincide con la consulta de suscripción, o cuando el usuario no tiene permisos suficientes para realizar la escritura, Realm revierte la escritura ilegal.

Cuando la escritura coincide con los permisos y la consulta de suscripción de Sincronización Flexible en el cliente, el SDK de Realm Swift puede escribir correctamente el objeto en el dominio sincronizado. Este objeto se sincroniza con el backend de App Services cuando el dispositivo tiene conexión de red.

// This write falls within the subscription query and complies
// with the Device Sync permissions, so this write should succeed.
do {
let learnRealm = Item()
learnRealm.ownerId = user.id
learnRealm.itemName = "Learn Realm CRUD stuff"
learnRealm.complexity = 3
try realm.write {
realm.add(learnRealm)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

En algunos casos, una escritura que inicialmente parece correcta es en realidad una escritura ilegal. En estos casos, el objeto escribe en la base de datos, pero cuando esta se sincroniza con el backend, Realm revierte la escritura mediante una operación de compensación de errores no fatales. Las escrituras de compensación pueden ocurrir cuando:

  • Las escrituras no coinciden con la suscripción de consulta: el objeto escrito coincide con los permisos del usuario, pero no coincide con la suscripción de consulta.

  • Guardados no coinciden con los permisos: El objeto guardado coincide con la suscripción de la query, pero no coincide con los permisos del usuario.

En más detalle, cuando se escriben datos que están fuera de los límites de una suscripción de consulta o no coinciden con los permisos del usuario, ocurre lo siguiente:

  1. Debido a que el ámbito del cliente no tiene el concepto de escrituras "ilegales", la escritura inicialmente tiene éxito hasta que el ámbito resuelve el conjunto de cambios con el backend de App Services.

  2. Tras la sincronización, el servidor aplica las reglas y los permisos. El servidor determina que el usuario no tiene autorización para realizar la escritura.

  3. El servidor envía una operación de reversión, denominada "escritura compensatoria", al cliente.

  4. El reino del cliente revierte la operación de escritura ilegal.

Cualquier guardado realizado del lado del cliente en un objeto determinado entre un guardado ilegal y el correspondiente guardado de compensación se perderá.

En la práctica, esto puede parecer como un objeto que se escribe en el reino y luego desaparece después de que el servidor envía la escritura compensatoria al cliente.

Para obtener más información sobre errores de permiso denegado, compensación de errores de escritura y otros tipos de errores de sincronización de dispositivos, consulte Errores de sincronización en la documentación de App Services.

Los registros de App Services contienen más información sobre por qué ocurre un error de escritura compensatoria.

Novedad en la versión 10.37.0.

Puedes obtener información adicional en el cliente sobre por qué ocurre una escritura compensatoria. El SDK de Swift expone un campo compensatingWriteInfo en un SyncError cuyo código es .writeRejected. Puedes acceder a esta información a través del manejador de errores de sincronización.

Este campo contiene un arreglo de objetos RLMCompensatingWriteInfo, los cuales proporcionan:

  • El objectType del objeto que el cliente intentó escribir

  • El primaryKey del objeto específico

  • El reason para el error de escritura compensatorio

Esta información es la misma que se encuentra en los registros de App Services. Se expone en el cliente para mayor comodidad y para fines de depuración.

Ejemplo

Este controlador de errores muestra un ejemplo de cómo puede registrar información sobre la compensación de errores de escritura:

myApp.syncManager.errorHandler = { syncError, session in
if let thisError = syncError as? SyncError {
switch thisError.code {
// ... additional SyncError.code cases ...
case .writeRejected:
if let compensatingWriteErrorInfo = thisError.compensatingWriteInfo {
for anError in compensatingWriteErrorInfo {
print("A write was rejected with a compensating write error")
print("The write to object type: \(anError.objectType)")
print("With primary key of: \(anError.primaryKey)")
print("Was rejected because: \(anError.reason)")
}
}
}
}
}

El controlador de errores de ejemplo anterior produce esta salida cuando ocurre un error de escritura compensatorio:

A write was rejected with a compensating write error
The write to object type: Optional("Item")
With primary key of: objectId(641382026852d9220b2e2bbf)
Was rejected because: Optional("write to \"641382026852d9220b2e2bbf\" in table \"Item\" not
allowed; object is outside of the current query view")

Solo se pueden escribir objetos en un dominio de sincronización flexible si coinciden con la consulta de suscripción. Si se realiza una escritura que no coincide con la consulta de suscripción, Realm escribe inicialmente el objeto, pero luego realiza una escritura compensatoria. Esta operación no fatal revierte una escritura no válida que no coincide con la consulta de suscripción.

En la práctica, esto puede parecer como si la escritura fuera exitosa, pero luego el objeto "desaparece" cuando Realm se sincroniza con el backend de App Services y realiza la escritura compensatoria.

Si desea escribir un objeto que no coincide con la suscripción de consulta, debe abrir un dominio diferente donde el objeto coincida con dicha suscripción. Como alternativa, puede escribir el objeto en un dominio no sincronizado que no aplique permisos ni consultas de suscripción.

Dada la configuración para el ámbito de sincronización flexible anterior, intentar escribir este objeto no coincide con la suscripción de consulta:

do {
let fixTheBug = Item()
fixTheBug.ownerId = user.id
fixTheBug.itemName = "Fix the bug with the failing method"
// The complexity of this item is `7`. This is outside the bounds
// of the subscription query, so this write triggers a compensating write.
fixTheBug.complexity = 7
try realm.write {
realm.add(fixTheBug)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

El mensaje de error en los registros del lado del cliente en este escenario es:

Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a
write that is outside of permissions or query filters; it has been
reverted" (error_code=231, try_again=true, error_action=Warning)

El mensaje de error en los registros de aplicación Services en este escenario es:

"FlexibleSync_Item": {
"63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\"
in table \"FlexibleSync_Item\" not allowed; object is outside of
the current query view"
}

Intentar escribir en el cliente también puede desencadenar un error de escritura compensatorio cuando el objeto no coincide con los permisos de escritura del lado del servidor del usuario.

En el cliente, este tipo de escritura se comporta igual que una escritura que no coincide con la suscripción de la consulta. En la práctica, puede parecer que la escritura se realizó correctamente, pero el objeto desaparece cuando Realm se sincroniza con el backend de App Services y realiza la escritura compensatoria.

Dados los permisos en la configuración de sincronización del dispositivo detallados anteriormente, intentar escribir un objeto donde la propiedad ownerId no coincide con la user.id del usuario que inició sesión no es una escritura legal:

do {
let itemWithWrongOwner = Item()
// The `ownerId` of this item does not match the `user.id` of the logged-in
// user. The user does not have permissions to make this write, so
// it triggers a compensating write.
itemWithWrongOwner.ownerId = "This string does not match the user.id"
itemWithWrongOwner.itemName = "Write code that generates a permission error"
itemWithWrongOwner.complexity = 1
try realm.write {
realm.add(itemWithWrongOwner)
}
} catch {
print("Failed to write to realm: \(error.localizedDescription)")
}

El error del cliente en este escenario es el mismo que cuando intenta escribir un objeto que está fuera del filtro de consulta:

Sync: Connection[1]: Session[1]: Received: ERROR "Client attempted a
write that is outside of permissions or query filters; it has been
reverted" (error_code=231, try_again=true, error_action=Warning)

El mensaje de error en los registros de App Services proporciona información adicional para ayudarle a determinar si se trata de un problema de permisos y no de suscripción a consultas. En este ejemplo, el mensaje de error indica que el objeto no coincide con el rol del usuario:

"FlexibleSync_Item": {
"63bdfc40f16be7b1e8c7e4b8": "write to \"63bdfc40f16be7b1e8c7e4b8\"
in table \"FlexibleSync_Item\" was denied by write filter in role
\"owner-read-write\""
}

Cada transacción de escritura para un conjunto de suscripciones tiene un coste de rendimiento. Si necesita realizar varias actualizaciones a un objeto de Realm durante una sesión, considere mantener los objetos editados en memoria hasta que se completen todos los cambios. Esto mejora el rendimiento de la sincronización al escribir solo el objeto completo y actualizado en su reino, en lugar de cada cambio.

Si desarrolla una aplicación que usa extensiones de aplicación, como una extensión compartida, evite escribir en un dominio sincronizado en esa extensión. Device Sync permite abrir un dominio sincronizado en un solo proceso como máximo. En la práctica, esto significa que si su aplicación usa un dominio sincronizado en una extensión de aplicación, podría fallar intermitentemente.

Si intenta abrir un dominio sincronizado en una extensión compartida u otra extensión de la aplicación, y dicho dominio no está abierto en la aplicación principal, es posible que la escritura desde una extensión compartida se realice correctamente. Sin embargo, si el dominio sincronizado ya está abierto en la aplicación principal o está sincronizando datos en segundo plano, podría aparecer un fallo relacionado con Realm::MultiSyncAgents. En este caso, es posible que deba reiniciar el dispositivo.

Si necesita leer o escribir en un reino sincronizado desde una extensión de aplicación, existen algunas alternativas recomendadas:

  • Primero sin conexión: pasa datos del disco hacia o desde la aplicación principal

  • Siempre actualizado: comuníquese directamente con la colección Atlas de respaldo a través de una conexión de red

Si la funcionalidad offline es la consideración más importante para tu aplicación, puedes transferir datos del disco hacia o desde tu aplicación principal. Podrías copiar objetos a un dominio no sincronizado y leerlos y compartirlos entre las aplicaciones de un grupo de aplicaciones. O bien, podrías usar una cola en disco para enviar los datos hacia o desde la aplicación principal y escribir solo en el dominio sincronizado desde allí. De esta manera, independientemente de la conectividad de red del dispositivo, la información se puede compartir en cualquier momento hacia o desde la extensión de la aplicación.

Si la información siempre actualizada en todos los dispositivos es fundamental para su aplicación, puede leer o escribir datos directamente desde o hacia la colección Atlas de respaldo a través de la red. Según sus necesidades, puede usar una de estas herramientas para comunicarse directamente con Atlas:

Entonces, cualquier dispositivo que tenga una conexión de red siempre obtendrá la información más actualizada, sin esperar a que el usuario abra su aplicación principal como en la opción anterior.

Esta opción requiere que el dispositivo del usuario tenga conexión a la red al usar la extensión de la aplicación. Como alternativa, puede comprobar si hay conexión a la red. Luego, utilice la opción en disco mencionada anteriormente si el dispositivo del usuario no tiene conexión a la red.

Volver

Administrar suscripciones de sincronización flexible