Este post explica cómo empezar con la replicación/sincronización de datos a través de dispositivos iOS usando Couchbase Mobile. Couchbase Mobile Stack se compone de Couchbase Server, Sync Gateway y Couchbase Lite embedded NoSQL Database. En un Correo electrónico:En el artículo anterior, discutimos cómo Couchbase Lite puede ser utilizado como una base de datos NoSQL independiente incrustada en aplicaciones iOS. Este post te guiará a través de un ejemplo de aplicación iOS en conjunto con un Sync Gateway que demostrará los conceptos básicos de Push & Pull Replication, Authentication & Access Control, Channels y Sync Functions.
Aunque vamos a ver la sincronización de datos en el contexto de una App iOS en Swift, todo lo que se discuta aquí es igualmente aplicable a apps móviles desarrolladas en cualquier otra plataforma (Android, iOS (ObjC), Xamarin). Las desviaciones se especificarán como tales.
NOTA: Estaremos discutiendo Couchbase Mobile v1.4 que es la versión de producción actual. Existe una versión Vista previa para desarrolladores versión 2.0 de Couchbase Mobile que tiene un montón de nuevas y emocionantes características.
Couchbase Móvil
Couchbase Mobile Stack está compuesto por Couchbase Server, Sync Gateway y Couchbase Lite embedded NoSQL Database. Este post tratará los conceptos básicos de NoSQL replicación y sincronización de datos usando Couchbase Mobile. Asumiré que estás familiarizado con el desarrollo de aplicaciones iOS, conceptos básicos de Swift, algunos conceptos básicos de NoSQL y que tienes algún conocimiento de Couchbase. Si quieres leer más sobre Couchbase Mobile, puedes encontrar un montón de recursos al final de este post.
Pasarela de sincronización Couchbase
Couchbase Sync Gateway es un mecanismo de sincronización orientado a Internet que sincroniza datos de forma segura entre dispositivos, así como entre dispositivos y la nube.
Expone una interfaz web que proporciona
- Sincronización de datos entre dispositivos y la nube
- Control de acceso
- Validación de datos
Puedes utilizar cualquier cliente HTTP para explorar más a fondo la interfaz. Eche un vistazo a Correo electrónico: sobre el uso de Postman para consultar la interfaz.
Hay tres conceptos principales relacionados con la replicación o sincronización de datos mediante Sync Gateway -
Canal
Un canal puede verse como una combinación de etiqueta y cola de mensajes. Cada documento puede asignarse a uno o varios canales. Los documentos se asignan a canales que especifican quién puede acceder a los documentos. Los usuarios tienen acceso a uno o varios canales y sólo pueden leer los documentos asignados a esos canales. Para más información, consulte la página documentación sobre Canales.
Función de sincronización
La función de sincronización es una función JavaScript que se ejecuta en Sync Gateway. Cada vez que se añade un nuevo documento, revisión o borrado a una base de datos, se llama a la función de sincronización. La función de sincronización es responsable de
- Validación del documento,
- Autorizar el cambio
- Asignación de documentos a canales y
- Conceder a los usuarios acceso a los canales.
Para más información, consulte documentación sobre la función de sincronización .
Replicación
La replicación, también conocida como sincronización, es el proceso de sincronizar los cambios entre la base de datos local y el Sync Gateway remoto. Existen dos tipos
- La replicación push se utiliza para transferir los cambios de la base de datos local a la remota.
- La replicación pull se utiliza para extraer cambios de la base de datos remota a la local.
Para más información, consulte documentación sobre réplicas.
Instalación de Couchbase Sync Gateway
Siga las guía de instalación para instalar el Sync Gateway.
Inicie su Sync Gateway con el siguiente archivo de configuración. La ubicación exacta del archivo de configuración dependerá de la plataforma. Consulte la guía de instalación.
Archivo de configuración de la pasarela de sincronización
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
{ "log": ["*"], "CORS": { "Origin":["*"] }, "databases": { "demo": { "server": "walrus:", "bucket": "default", "users": { "GUEST": { "disabled": true, "admin_channels": ["*"] } , "joe": {"password":"password" ,"disabled": false, "admin_channels":["_public","_joe"]} , "jane":{"password":"password" ,"disabled": false, "admin_channels":["_public","_jane"]} }, "unsupported": { "user_views": { "enabled":true } }, "sync": ` function (doc, oldDoc){ // Check if doc is being deleted if (doc._deleted == undefined) { // Validate current version has relevant keys validateDocument(doc); } else { // Validate old document has relevant keys validateDocument(oldDoc); } var docOwner = (doc._deleted == undefined) ? doc.owner : oldDoc.owner; var publicChannel = "_public"; var privateChannel = "_"+docOwner; // Grant user read access to public channels and user's own channel access(docOwner,[publicChannel,privateChannel]); // Check if this was a doc update (as opposed to a doc create or delete) if (doc._deleted == undefined && oldDoc != null && oldDoc._deleted == undefined) { if (doc.tag != oldDoc.tag) { throw({forbidden: "Cannot change tag of document"}); } } // Check if new/updated document is tagged as "public" var docTag = (doc._deleted == undefined) ? doc.tag : oldDoc.tag; if (doc._deleted == undefined) { if (docTag == "public") { // All documents tagged public go into "public" channel which to open to all channel(publicChannel); } else { // Ensure that the owner of document is the user making the request requireUser(docOwner); // All non-public tagged docs go into a user user specific channel channel(privateChannel); } } else { channel(doc.channels); } function validateDocument (doc) { // Basic validation of document if (!doc.tag ) { // Every doc must include a tag throw({forbidden: "Invalid document type: Tag not provided" + doc.tag}); } if (!doc.owner) { // Every doc must include a owner throw({forbidden: "Invalid document type: Owner not provided" + doc.owner}); } } } ` } } } |
Estos son algunos puntos clave que hay que tener en cuenta en el archivo de configuración:-
- Línea 8: El valor "walrus:" para "server" indica que el Sync Gateway debe persistir los datos en memoria y no está respaldado por un servidor Couchbase.
- Línea 11: El acceso de usuarios invitados está desactivado
- Líneas 12-13: Hay dos usuarios, "Jane" y "Joe" configurados en el sistema. Ambos usuarios tienen acceso a un canal "_público" y cada uno tiene acceso a su propio canal privado.
- Línea 22-100: Una simple función de sincronización que hace lo siguiente
- Líneas 29-36 : Validación del documento para garantizar que contiene las propiedades "tag" y "owner" definidas por el usuario.
- La propiedad "tag" se utiliza para especificar si el documento está disponible públicamente para cualquier usuario o si es privado para un usuario
- La propiedad "owner" se utiliza para especificar si el documento está disponible públicamente para cualquier usuario o si es privado para un usuario
- Línea 46: Dar acceso al usuario al canal "_public" y a un canal privado (identificado mediante el propietario del documento).
- Líneas 51-56 : Si se trata de una actualización del documento, compruebe que la propiedad "tag" no se modifica en las distintas revisiones.
- Línea 66: Asignar todos los documentos con etiqueta "public" al canal "_public".
- Línea 72: Asignar todos los documentos con una etiqueta distinta de "público" al canal privado.
- Línea 75: En el caso de los documentos de canal privado, compruebe en primer lugar que el titular del documento es quien realiza la solicitud
- Líneas 29-36 : Validación del documento para garantizar que contiene las propiedades "tag" y "owner" definidas por el usuario.
Couchbase Lite
Couchbase Lite es una base de datos NoSQL integrada que se ejecuta en dispositivos. Couchbase Lite se puede utilizar en varios modos de despliegue. Introducción a Couchbase Lite Correo electrónico: habla del modo de despliegue autónomo. Couchbase Lite puede ser usado en conjunto con un Sync Gateway remoto que le permitiría sincronizar datos a través de dispositivos. Este post trata sobre el modo de despliegue usando un Sync Gateway.
Hay muchas opciones para integrar el framework Couchbase Lite en tu aplicación iOS. Echa un vistazo a nuestro Couchbase Mobile Guía de iniciación para las distintas opciones de integración.
API nativa
Couchbase Lite expone una API nativa para iOS, Android y Windows que permite a las aplicaciones interactuar fácilmente con la plataforma Couchbase. Como desarrollador de aplicaciones, no tienes que preocuparte por el funcionamiento interno de la base de datos integrada de Couchbase Lite, sino que puedes centrarte en crear tu aplicación. La API nativa te permite interactuar con el framework de Couchbase Lite como lo harías con otros frameworks/subsistemas de la plataforma. De nuevo, hablaremos de Couchbase Mobile v1.4 en esta entrada del blog. Puedes obtener una lista completa de las APIs en nuestro Couchbase Desarrollador sitio.
Demo iOS App
Descargue el proyecto de demostración de Xcode desde Repo de Github y cambiar a la rama "soporte de sincronización". Usaremos esta app como ejemplo en el resto del blog. Esta aplicación utiliza Cocoapods para integrar el framework Couchbase Lite.
|
1 2 |
git clone git@github.com:couchbaselabs/couchbase-lite-ios-standalone-sampleapp.git git checkout syncsupport |

Sincronización de documentos entre usuarios
- Cree y ejecute la aplicación. Debería aparecer una alerta de inicio de sesión
- Introduzca el usuario "jane" y la contraseña de "password" . Este usuario se configuró en el archivo de configuración de Sync Gateway
- Añada el primer documento pulsando el botón "+" en la esquina superior derecha.
- Dé un nombre al documento y una descripción de una línea.
- Utiliza la etiqueta "privado".
- Entre bastidores, el replicador push envía el documento a la pasarela de sincronización y es procesado por la función de sincronización. Basándose en la etiqueta, la función de sincronización asigna el documento al canal privado del usuario.
- Añada un segundo documento pulsando el botón "+" en la esquina superior derecha.
- Dar un nombre al documento y una descripción de una línea
- Utiliza la etiqueta "public".
- Entre bastidores, el replicador push envía el documento a la pasarela de sincronización y es procesado por la función de sincronización. Basándose en la etiqueta pública, la función Sync asigna el documento al canal público
- Ahora "cierra sesión" Jane . Aparecerá de nuevo la alerta de inicio de sesión
- Introduzca el usuario "joe" y la contraseña "password". Este usuario también se configuró en el archivo de configuración de Sync Gateway
- Aparecerá el documento público creado por Jane.
- Entre bastidores, el Pull Replicator extrae todos los documentos del canal privado de Joe y del canal público. Se extrae el documento público creado por Jane. Sin embargo, como Joe no tenía acceso al canal privado de Jane, el documento privado creado por Jane no se extrae.
Para verificar el estado de las cosas en Sync Gateway, puede utilizar la interfaz REST de administración mediante Postman o cualquier cliente HTTP.
Esta es la petición CURL a la pasarela de sincronización
|
1 2 3 4 5 |
curl -X GET \ 'https://localhost:4985/demo/_all_docs?access=false&channels=false&include_docs=true' \ -H 'accept: application/json' \ -H 'cache-control: no-cache' \ -H 'content-type: application/json' |
La respuesta de la pasarela de sincronización muestra los dos documentos asignados al canal público y privado de Jane, respectivamente
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
{ "rows": [ { "key": "-6gCouN6jj0ScYgpMD7Qj1a", "id": "-6gCouN6jj0ScYgpMD7Qj1a", "value": { "rev": "1-dfa6d453a1515ee3dd64012ccaf53046", "channels": [ "_jane" ] }, "doc": { "_id": "-6gCouN6jj0ScYgpMD7Qj1a", "_rev": "1-dfa6d453a1515ee3dd64012ccaf53046", "name": "doc101", "overview": "This is a private doc from Jane", "owner": "jane", "tag": "private" } }, { "key": "-A2wR44pAFCdu1Yufx14_1S", "id": "-A2wR44pAFCdu1Yufx14_1S", "value": { "rev": "1-1a8cd0ea3b7574cf6f7ba4a10152a466", "channels": [ "_public" ] }, "doc": { "_id": "-A2wR44pAFCdu1Yufx14_1S", "_rev": "1-1a8cd0ea3b7574cf6f7ba4a10152a466", "name": "doc102", "overview": "This is a public doc shared by Jane", "owner": "jane", "tag": "public" } } ], "total_rows": 2, "update_seq": 5 } |
Explorar el código
Ahora, vamos a examinar fragmentos de código relevantes de la Demo App iOS -
Abrir/crear una base de datos por usuario
Abrir DocListTableViewController.swift y localice openDatabaseForUser función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
do { // 1: Set Database Options let options = CBLDatabaseOptions() options.storageType = kCBLSQLiteStorage options.create = true // 2: Create a DB for logged in user if it does not exist else return handle to existing one self.db = try cbManager.openDatabaseNamed(user.lowercased(), with: options) self.showAlertWithTitle(NSLocalizedString("Success!", comment: ""), message: NSLocalizedString("Database \(user) was opened succesfully at path \(CBLManager.defaultDirectory())", comment: "")) // 3. Start replication with remote Sync Gateway startDatabaseReplicationForUser(user, password: password) return true } catch { // handle error } |
- Especifica las opciones a asociar con la base de datos. Explore las demás opciones de la clase CBLDatabaseOptions.
- Crea una base de datos con el nombre del usuario actual. De esta forma, cada usuario de la aplicación tendrá su propia copia local de la base de datos. Si existe una base de datos con el nombre, se devolverá un "handle" a la base de datos existente, de lo contrario se creará una nueva. Los nombres de las bases de datos deben estar en minúsculas. Si tiene éxito, se creará una nueva base de datos local si no existe. Por defecto, la base de datos se creará en la ruta por defecto (/Library/Application Support). Puede especificar un directorio diferente al instanciar la aplicación CBLManager clase.
- Inicia el Proceso de Replicación de la Base de Datos para las credenciales de usuario dadas. Discutiremos el código de Replicación en detalle en las siguientes secciones.
Búsqueda de documentos
Abra el DocListTableViewController.swift y localice getAllDocumentForUserDatabase función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// 1. Create Query to fetch all documents. You can set a number of properties on the query object liveQuery = self.db?.createAllDocumentsQuery().asLive() guard let liveQuery = liveQuery else { return } // 2: You can optionally set a number of properties on the query object. // Explore other properties on the query object liveQuery.limit = UInt(UINT32_MAX) // All documents // query.postFilter = //3. Start observing for changes to the database self.addLiveQueryObserverAndStartObserving() // 4: Run the query to fetch documents asynchronously liveQuery.runAsync({ (enumerator, error) in switch error { case nil: // 5: The "enumerator" is of type CBLQueryEnumerator and is an enumerator for the results self.docsEnumerator = enumerator default: self.showAlertWithTitle(NSLocalizedString("Data Fetch Error!", comment: ""), message: error.localizedDescription) } }) } catch { // handle error } |
- Obtener el acceso a la base de datos con el nombre especificado
- Crear un objeto de consulta. Esta consulta se utiliza para obtener todos los documentos. La función de sincronización de la puerta de enlace de sincronización garantizará que los documentos se extraigan únicamente de los canales accesibles para el usuario. Puede crear un objeto de consulta normal o un objeto de consulta "en vivo". El objeto de consulta "en vivo" es de tipo CBLLiveQuery que se actualiza automáticamente cada vez que se producen cambios en la base de datos que afectan a los resultados de la consulta. La consulta tiene una serie de propiedades que se pueden modificar para personalizar los resultados. Pruebe a modificar las propiedades y vea el efecto en los resultados
- Tendrá que añadir explícitamente un observador al objeto Live Query para ser notificado de los cambios en la base de datos. Discutiremos esto más a fondo en la sección "Observación de cambios sincronizados locales y remotos en los documentos". No olvide eliminar el observador y dejar de observar los cambios cuando ya no lo necesite.
- Ejecute la consulta de forma asíncrona. También puede hacerlo de forma sincrónica si lo prefiere, pero probablemente sea recomendable hacerlo de forma asincrónica si los conjuntos de datos son grandes.
Una vez que la consulta se ejecuta correctamente, se obtiene un objeto CBLQueryEnumerator. El enumerador de consultas permite enumerar los resultados. Se presta muy bien como fuente de datos para la vista de tabla que muestra los resultados
Observación de cambios sincronizados locales y remotos en los documentos
Abra el DocListTableViewController.swift y localice la función addLiveQueryObserverAndStartObserving.
Los cambios en la base de datos pueden ser el resultado de las acciones del usuario en el dispositivo local o pueden ser el resultado de cambios sincronizados desde otros dispositivos.
|
1 2 3 4 5 |
// 1. iOS Specific. Add observer to the live Query object liveQuery.addObserver(self, forKeyPath: "rows", options: NSKeyValueObservingOptions.new, context: nil) // 2. Start observing changes liveQuery.start() |
- Para recibir notificaciones de los cambios en la base de datos que afectan a los resultados de la consulta, añada un observador al objeto Live Query. En su lugar, aprovecharemos el patrón Key-Value-Observer de iOS para ser notificados de los cambios. Añadir un observador KVO al objeto Live Query para empezar a observar los cambios en la propiedad "rows" del objeto Live Query Esto se gestiona a través de las APIs de Event Handler apropiadas en otras plataformas, como la plataforma addChangeListener en Android/Java.
- Empieza a observar los cambios .
Siempre que se produzca un cambio en la base de datos que afecte a la propiedad "rows" del objeto LiveQuery, tu aplicación recibirá una notificación de cambios. Cuando recibas la notificación de cambio, puedes actualizar tu UI, que en este caso sería recargar la tableview.
|
1 2 3 4 |
if keyPath == "rows" { self.docsEnumerator = self.liveQuery?.rows tableView.reloadData() } |
Autenticación de las solicitudes de réplica
Abrir Archivo DocListTableViewController.swift y localizar startDatabaseReplicationForUser función.
Todas las solicitudes de replicación deben estar autenticadas. En esta aplicación, utilizamos la autenticación básica HTTP.
|
1 |
let auth = CBLAuthenticator.basicAuthenticator(withName: user, password: password) |
Existen varios tipos de autenticador: Basic, Facebook, OAuth1, Persona, SSL/TLS Cert.
Replicación Pull
Abrir Archivo DocListTableViewController.swift y localizar startPullReplicationWithAuthenticator función.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 1: Create a Pull replication to start pulling from remote source let pullRepl = db?.createPullReplication(URL(string: kDbName, relativeTo: URL.init(string: kRemoteSyncUrl))!) // 2. Set Authenticator for pull replication pullRepl?.authenticator = auth // Continuously look for changes pullRepl?.continuous = true // Optionally, Set channels from which to pull // pullRepl?.channels = [...] // 4. Start the pull replicator pullRepl?.start() |
- Cree un replicador de extracción para extraer los cambios de la puerta de enlace de sincronización remota. La dirección kRemoteSyncUrl es la URL del punto final de la base de datos remota en Sync Gateway.
- Asociar Autenticador con la Replicación Pull. Opcionalmente se pueden establecer los canales de los que se extraerán los documentos
- Configurar la replicación como "continua" permitirá que las actualizaciones de cambios se extraigan indefinidamente a menos que se detenga explícitamente o se cierre la base de datos.
- Iniciar la replicación Pull
Replicación Push
Abrir Archivo DocListTableViewController.swift y localizar startPushReplicationWithAuthenticator función.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// 1: Create a push replication to start pushing to remote source let pushRepl = db?.createPushReplication(URL(string: kDbName, relativeTo: URL.init(string:kRemoteSyncUrl))!) // 2. Set Authenticator for push replication pushRepl?.authenticator = auth // Continuously push changes pushRepl?.continuous = true // 3. Start the push replicator pushRepl?.start() |
- Cree un replicador push para enviar los cambios a la puerta de enlace de sincronización remota. La dirección kRemoteSyncUrl es la URL del punto final de la base de datos remota en Sync Gateway.
- Asociar autenticador con la replicación push.
- Configurar la replicación como "continua" permitirá que las actualizaciones de cambios se envíen indefinidamente a menos que se detenga explícitamente o se cierre la base de datos.
- Iniciar la replicación push
Supervisión del estado de la replicación
Abra el DBListTableViewController.swift y localice la función addRemoteDatabaseChangesObserverAndStartObserving.
|
1 2 3 4 5 6 7 |
// 1. iOS Specific. Add observer to the NOtification Center to observe replicator changes NotificationCenter.default.addObserver(forName: NSNotification.Name.cblReplicationChange, object: db, queue: nil) { [unowned self] (notification) in // Handle changes to the replicator status - Such as displaying progress // indicator when status is .running } |
Puede supervisar el estado de la replicación añadiendo un observador al Centro de notificaciones de iOS para recibir notificaciones de cblReplicationChange notificaciones . Usted podría utilizar el controlador de notificación, por ejemplo, para mostrar indicadores de progreso apropiados para el usuario. Esto se maneja a través de las APIs de manejadores de eventos apropiados en otras plataformas como el addChangeListener en Android/Java.
¿Y ahora qué?
Nos encantaría conocer su opinión. Así que si tienes preguntas o comentarios, no dudes en ponerte en contacto conmigo en Twitter @rajagp o envíeme un correo electrónico priya.rajagopal@couchbase.com. Si desea mejorar la aplicación de demostración, envíe una solicitud de extracción a la base de datos Repo de Github.
En Foros de desarrollo de Couchbase Mobile es otro gran lugar para obtener respuestas a sus preguntas relacionadas con los móviles. Consulte el portal de desarrollo para obtener más información sobre el Pasarela de sincronización y Couchbase Lite . Todo lo que se discutió aquí está en el contexto de Couchbase Mobile 1.4. Hay un montón de cambios nuevos y emocionantes que vienen en Couchbase Mobile 2.0. Asegúrate de revisar el Vista previa para desarrolladores versión 2.0 de Couchbase Mobile.
¡¡¡Hola Priya ..!!!
Eso fue un muy buen post para la sincronización de datos en Swift.If se puede proporcionar en C objetivo también, sería apreciada.
Gracias.
Hola Priya,
He descargado e instalado la aplicación UserProfileDemo para Xamarin y he intentado probarla ejecutándola en dos simuladores simultáneamente. Los datos no se sincronizan cuando se inicia sesión con las mismas credenciales en el otro simulador. se obtiene obtener los datos de la base de datos local cada vez, pero no funciona la sincronización global.
Tu comentario no parece estar relacionado con esta entrada del blog y el tutorial (que es, de hecho, para iOS nativo en Couchbase Mobile 1.4). Has seguido las instrucciones de este tutorial de Xamarin? https://docs.couchbase.com/userprofile-couchbase-mobile/sync/userprofile/xamarin/userprofile_sync.html. Si tiene problemas con este tutorial, publíquelo en nuestros foros de desarrollo (forums.couchbase.com) con los registros pertinentes.