Overview
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.
Cómo determinar qué datos se sincronizan
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:
Configuración de servicios de aplicaciones
La sincronización del dispositivo está configurada con los siguientes campos consultables:
_id(siempre incluido)complexityownerId
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 }
Modelo de datos del cliente y configuración
Los ejemplos de esta página utilizan el siguiente modelo de objetos:
class Item: Object { (primaryKey: true) var _id: ObjectId var ownerId: String var itemName: String 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)") }
¿Qué datos se sincronizan?
La consulta de suscripción combinada con los permisos significa que el reino sincronizado solo sincroniza objetos donde:
El
ownerIdcoincide con eluser.iddel usuario que inició sesión (de los permisos)El valor de la propiedad
complexityes menor o igual a4(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.
Escribir en un reino sincronizado
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.
Escrituras exitosas
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)") }
Escrituras compensatorias
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:
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.
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.
El servidor envía una operación de reversión, denominada "escritura compensatoria", al cliente.
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.
Compensación de información de error de escritura
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
objectTypedel objeto que el cliente intentó escribirEl
primaryKeydel objeto específicoEl
reasonpara 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")
El
Optional("Item")en este mensaje es unItemobjeto utilizado en la aplicación de plantilla Swift.La clave principal es
objectIddel objeto específico que el cliente intentó escribir.table \"Item"\hace referencia a la colección Atlas donde se sincronizaría este objeto.La razón por la que
object is outside of the current query viewen este ejemplo es porque la suscripción de consulta se configuró para requerir que la propiedadisCompletedel objeto fueratrue, y el cliente intentó guardar un objeto donde esa propiedad erafalse.
Operaciones de escritura que no coinciden con la suscripción de query
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.
Ejemplo de código
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)") }
Error del cliente
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)
Error de servicios de aplicaciones
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" }
Escrituras que no coinciden con los permisos
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.
Ejemplo de código
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)") }
Error del cliente
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)
Error de servicios de aplicaciones
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\"" }
Escritura en grupo para mejorar el rendimiento
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.
No escriba en un dominio sincronizado en una extensión de aplicación
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.
Fallos relacionados con la apertura de un dominio sincronizado en varios procesos
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.
Alternativas a escribir en un dominio sincronizado en una extensión de aplicación
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
Pasar datos en el disco
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.
Comunicarse directamente con la Colección Atlas de Apoyo
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:
Consulta Atlas con el SDK Swift de Realm MongoClient
Pasar datos a una función de App Services
Realizar llamadas HTTPS con la API de datos
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.