Saltar a contenido

iCloud y CloudKit

iCloud

iCloud es un servicio de Apple que permite a un usuario acceder a su contenido personal (datos, documentos) en todos sus dispositivos utilizando su Apple ID.

iCloud consigue esto combinando almacenamiento en la nube y APIs dedicadas integradas en el sistema operativo.

Apple proporciona la infraestructura de servidores, de transmisión de datos y de cuentas de usuario, facilitando el trabajo a los desarrolladores que no necesitan crear sus propios servicios ni recurrir a soluciones de terceros.

Filosofía de iCloud para el usuario de iOS

Un escenario frecuente es que un usuario tenga una app instalada en más de un dispositivo (un iPhone y un iPad, por ejemplo). Por ejemplo es muy común usar la app de fotos, la del calendario, o las notas en cualquier dispositivo.

En este escenario, la idea principal de iCloud es que el usuario pueda pasar de un dispositivo a otro y seguir trabajando con el mismo estado de la app tal y como la dejó en el dispositivo anterior.

Para el usuario, los cambios aparecen automáticamente en todos los dispositivos conectados a la cuenta iCloud.

Cuenta iCloud

Todo usuario de Apple puede activar una cuenta de iCloud usando su Apple ID. Casi todos los usuarios de dispositivos Apple tienen activada esta cuenta.

Permite mantener el estado en aplicaciones ejecutándose en distintos dispositivos asociados al mismo Apple ID: Recordatorios, Notas, etc.

El sistema operativo encripta todos los datos antes de transmitirlos a los servidores de iCloud, los cuales almacenan los datos también en formato encriptado. Se utilizan tokens para la autenticación.

De forma gratuita Apple proporciona 5Gb de espacio en la cuenta de iCloud.

Nota

La cuenta de iCloud puede activarse desde el simulador. Si es la primera vez que usas iCloud desde el simulador debes logearte con tu Apple Id en icloud.com y aceptar los términos.

Distintas APIs

Bajo el nombre genérico de iCloud, existen distintas APIs que Apple ha ido proporcionando a los desarrolladores para gestionar datos asociados a la cuenta de usuario:

  • Almacenamiento clave-valor en iCloud: para mantener el estado de la aplicación (puntuación de un juego, última página leída, etc.).
  • Documentos en iCloud: para gestionar documentos en la nube y mantenerlos sincronizados entre iPhone/iPad/Mac.
  • iCloud con Core Data: para mantener de forma automática en iCloud una copia de todos los datos de la app gestionados con Core Data. Versión inicial con muchos problemas, muy mejorado en las últimas versiones.
  • CloudKit: nueva tecnología a partir de iOS 8 que permite mayor flexibilidad y control. Basado en la gestión en la nube de registros con diccionarios clave-valor, con un enfoque muy similar a las tecnologías NoSQL.
    • Se trata de un API de peticiones a los servidores en la nube, que no mantiene un estado local. Es conveniente usarla en combinación con Core Data, si queremos hacer persistentes en local los datos existentes en la nube.
    • Basada en peticiones y respuestas asíncronas.

Vamos a ver en esta sesión el almacenamiento clave-valor y CloudKit, que son las APIs más usadas.

Preparación de aprovisionamiento y permisos para iCloud

Para desarrollar con iCloud es necesario estar registrado como desarrollador en el programa de desarrollo de Apple. También puedes hacerlo con tu Apple ID registrado en el equipo de la UA.

Para usar los servicios de iCloud es necesario crear un perfil de aprovisionamiento con un App Id concreto, añadir el servicio de iCloud y activar el permiso (capabilities) en la app con XCode.

Si estás registrado en el equipo de desarrollo con un rol de administrador (o tienes una cuenta de pago en la que tienes todos los permisos de tu equipo), se puede hacer todo automáticamente desde Xcode.

Creación del App ID

Para trabajar con iCloud clave-valor puedes utilizar el perfil de aprovisionamiento Master Moviles iCloud creado en el member center del equipo de la universidad. El bundle ID de la app debe ser es.ua.mastermoviles.iCloud.

También hemos actualizado el perfil Master Moviles ToDoList para incluir los permisos de uso de iCloud y CloudKit.

Se debe crear el App ID que otorgue la capacidad de acceso a iCloud.

Hemos creado el permiso (App ID) Master Moviles iCloud con el bundle name es.ua.mastermoviles.iCloud que incluye la capacidad de iCloud.

Aparece un botón junto al permiso de iCloud porque requiere una configuración posterior relacionada con CloudKit (lo veremos más adelante). Pero es suficiente para trabajar con iCloud clave-valor.

iCloud clave-valor

API de almacenamiento clave-valor

Permite guardar y recuperar en iCloud claves y valores desde los dispositivos en los que el usuario está registrado con su Apple Id.

Para gestionar estos valores debemos usar la clase NSUbiquitousKeyValueStore.

Puedes almacenar Strings, valores escalares como BOOL o Double, diccionarios y también objetos de cualquiera de los siguientes tipos: NSNumber, NSString, NSDate, NSData, NSArray, or NSDictionary.

El espacio de almacenamiento total, para un usuario dado y una app, es de 1 MB y un máximo de 1024 claves.

Para obtener el objeto compartido iCloudKeyValueStore:

let iCloudStore = NSUbiquitousKeyValueStore.default

Método synchronize

func synchronize() -> Bool

Devuelve true si las claves y valores en memoria y en disco están sincronizados o false si ha sucedido algún error. Por ejemplo, devuelve false si la app no se ha compilado con las peticiones adecuadas de entitlement o si el usuario no está logeado e iCloud.

Los cambios al almacén de claves-valor se salvan en memoria. El sistema sincroniza automáticamente estos datos con la caché del disco en los momentos apropiados. Por ejemplo, cuando el app pasa a segundo plano o cuando se reciben cambios de iCloud.

Este método no fuerza la subida a iCloud de los nuevos valores y claves, sino que hace saber a iCloud que los valores están listos para ser subidos. El sistema controla cuándo subir los datos.

No es obligatorio su uso, pero es recomendable cuando estamos trabajando con el simulador para asegurarnos de que el almacén de claves-valor se guarda.

Se recomiendo también hacerlo después de lanzar la app o cuando vuelve al primer plano.

Ejemplo de uso de synchronize al lanzar la app

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let store = NSUbiquitousKeyValueStore.default

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        if (store.synchronize()) {
            print("Sincronización OK")
        } else {
            print("Problemas en la sincronización")
        }
    }

    ...

}

Guardar valores en el almacén de claves-valor

Para actualizar los valores hay que usar los métodos set. El primer parámetro es el valor a guardar y el segundo la clave:

  • set(Bool, forKey: String)
  • set(Double, forKey: String)
  • set(Int64, forKey: String)
  • set([Any]?, forKey: String)
  • ...

Por ejemplo, set(Int64, forKey: String) actualiza en el almacén el valor long long (Int64) asociándolo a una clave especificada:

store.set(100, forKey: "puntuacion")

Obtención de valores del almacén de claves-valor

Funciones que obtienen los distintos tipos de datos a partir de una clave (una cadena):

  • array(forKey: String) -> [Any]?
  • bool(forKey: String) -> Bool
  • dictionary(forKey: String) -> [String : Any]?
  • string(forKey: String) -> String?
  • longLong(forKey: String) -> Int64
  • ...

forKey es el String que es la clave en el almacén de claves-valor.

Devuelve el valor asociado a la clave o nil si la clave no existe (0 en el caso de los métodos que devuelven un valor numérico).

Por ejemplo, longlong(forKey: String) devuelve el valor Int64 asociado a una clave especificada:

let puntuacion = Int(store.longLong(forKey:"puntuacion"))

En la aplicación ejemplo que veremos después en la demostración tenemos el siguiente código que sincroniza el valor actual de la interfaz de usuario (una etiqueta con un valor) con el valor del almacén de iCloud.

class ViewController: UIViewController {

    let store = NSUbiquitousKeyValueStore.default

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let valoriCloud = Int(store.longLong(forKey: "valor"))
        print("Valor cargado de iCloud: \(valoriCloud)")
        self.muestra(valor: valoriCloud)
    }

    @IBAction func pulsadoStepper(_ sender: UIStepper) {
        let valorPulsado: Int = Int(sender.value)
        store.set(valorPulsado, forKey: "valor")
        store.synchronize()
        self.muestra(valor: valorPulsado)
    }

Definición de un observador de cambios

Además de almacenar los valores podemos recibir notificaciones (NSNotification gestionadas por el NotificationCenter) de cambio de los valores en otros dispositivos conectados a iCloud.

En el lanzamiento del app hay que registrarse para la notificación NSUbiquitousKeyValueStoreDidChangeExternallyNotification.

La notificación se envía cuando el valor de una o más claves han cambiado debido a datos que han llegado desde iCloud. La notificación no se envía cuando la propia app ha cambiado los valores.

El diccionario atributo userInfo de la notificación contiene la razón de la notificación, así como una lista de los valores cambiados.

El objeto en la notificación es el NSUbiquitousKeyValueStore cuyo contenido ha cambiado.

Por ejemplo, en el siguiente código se registra como observador un método de la propia clase AppDelegate:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let store = NSUbiquitousKeyValueStore.default

    func application(_ application: UIApplication, 
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        if (store.synchronize()) {
            print("Sincronización OK")
        } else {
            print("Problemas en la sincronización")
        }
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(muestraValoriCloud(notification:)),
            name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
            object: nil)
        return true
    }

    @objc func muestraValoriCloud(notification: Notification){
        let valoriCloud = Int(store.longLong(forKey: "valor"))
        print("Recibida notificación del sistema con el valor: \(valoriCloud)")
        // Actualizamos el valor en el controller
        DispatchQueue.main.async {
            let application = UIApplication.shared
            if let controller = application.windows[0].rootViewController {
                let miController: ViewController = controller as! ViewController
                miController.muestra(valor: valoriCloud)
            }
        }
    }

Demo

  • Descargamos la app iCloudKeyValue desde este enlace, comentamos su funcionamiento y añadimos el código para incorporar iCloud clave valor:
    • Primero para que vaya grabando en iCloud el último número pulsado, y para que se obtenga cuando arranca la app. Lo probamos primero en el simulador y después en el dispositivo físico.
    • Nos logeamos con nuestra cuenta de iCloud en el simulador y probamos si se comparte la información con el dispositivo físico.
    • Nos logeamos después en otro dispositivo físico (un iPad) y probamos si se comparte la información entre los dos dispositivos físicos.
    • Por último añadimos el código para que se actualice la información entre dos dispositivos con una notificación del sistema.

CloudKit

Introducción a CloudKit

El origen de CloudKit es un proyecto interno de Apple en el que se basan muchas de sus APIs de persistencia. Su uso se ofrece a les desarrolladores en la WWDC de 2014, para apps a partir de iOS 8.

Permite gestionar datos remotos ubicados en los servidores de iCloud propios de Apple.

En los datos propios de la aplicación (datos privados del usuario) el almacenamiento se imputa a las cuentas iCloud de los usuarios.

Existe la posibilidad de datos públicos, en un almacenamiento gestionado por el desarrollador (gratuito hasta una capacidad y de pago a partir de ella).

Permite datos estructurados y datos bulk.

CloudKit proporciona una base de datos NoSQL en la nube, mediante la que las aplicaciones pueden guardar, consultar y realizar búsquedas de registros.

Tecnología de transporte

CloudKit no proporciona ninguna forma de almacenar datos localmente.

Los datos obtenidos se almacenan en la aplicación. Si queremos hacerlos persistentes de forma local (para que estén disponibles sin conexión) podemos utilizar otra tecnología como Core Data.

Es un servicio para mover datos a y desde la nube iCloud y no está pensado para reemplazar los modelos de datos ya existentes en tu app (CoreData).

El objetivo del framework es complementar estos modelos con una forma de empaquetar los datos para la nube y recibir actualizaciones posteriores sobre esos datos.

Con CloudKit, tu eres el responsable de mover los datos desde tu app a iCloud y desde iCloud a la app. Aunque CloudKit proporciona facilidades para mantenerte informado cuando sucede un cambio, tu debes obtener esos cambios explícitamente.

Debido a que eres el responsable de obtener y salvar los datos, debes de asegurarte de que los datos se obtienen en el momento oportuno y en el orden correcto, y de manejar los errores que se producen.

Elementos de CloudKit

  • Contenedores
  • Bases de datos
  • Registros
  • Zonas de registros
  • Identificadores
  • Referencias

Contenedores

Múltiples apps y usuarios tienen acceso a iCloud, pero los datos se encuentran segregados y encapsulados en particiones llamadas contenedores. En general, un contenedor va a contener todos los datos públicos y de usuarios de una aplicación. También es posible que más de una app del mismo desarrollador compartan un único contenedor.

Los datos se encuentran almacenados en un contenedor. Los contenedores de tus apps no pueden ser usados por apps de otro desarrollador.

Es posible compartir un contenedor entre varias apps, siempre que hayan sido desarrolladas por el mismo desarrollador.

Cada contenedor tiene un nombre único que por defecto está asociado al bundle id de la app. El nombre es iCloud.<bundleId>.

Los contenedores no pueden borrarse. En el portal de desarrolladores de la UA hemos acumulado muchos nombres antiguos de contenedores que ya no se usan.

Clase CKContainer

La clase con la que trabajar para gestionar el contenedor es CKContainer

La debemos usar para:

  • Obtener las bases de datos públicas y privadas
  • Obtener el identificador del contenedor
  • Determinar el estado del acceso de la cuenta iCloud del usuario
  • Solicitar y determinar permisos de la app
  • Ejecutar operaciones sobre el contenedor
  • Descubrir registros de usuarios

En CloudKit todas las operaciones son asíncronas: se pasa el código de callback al que se llamará cuando la petición devuelva la respuesta.

Datos públicos y privados

Se pueden guardar datos de forma pública y privada, dependiendo de si se guardan en la base de datos pública o en la privada.

Los datos públicos son accesibles a todos los usuarios de la app, aunque el usuario no se haya identificado con su cuenta de iCloud.

Los datos privados son sólo visibles por el usuario actual logeado en iCloud.

Para salvar datos en la base de datos pública es necesario que el usuario esté identificado, porque siempre se guarda el usuario propietario del registro.

Bases de datos

Las bases de datos son instancias de la clase CKDatabase

Cada app tiene acceso a dos bases de datos:

  • Base de datos pública
  • Base de datos privada

Se obtienen a través del CKContainer:

let container = CKContainer.default()
let privateDB = container.privateCloudDatabase
let publicDB = container.publicCloudDatabase

Dashboard

El CloudKit Dashboard es una interfaz web con la que podemos gestionar nuestros contenedores y bases de datos. Podemos acceder desde la cuenta de desarrollador de la universidad:

La interfaz web permite:

  • Crear, visualizar, editar y borrar tipos de registros, registros, etc.
  • Estadísticas de uso
  • Administración de acceso Configuración de despliegue

Ejemplo de visualización de tipos de registros:

CloudKit trabaja sobre registros en iCloud

CloudKit proporciona una forma de mover datos estructurados entre tu aplicación y iCloud.

A diferencia de las bases de datos relacionales tradicionales, en las que el modelo de datos se basa en tablas, en CloudKit se trabaja con tipos de registros.

Un tipo de registro se define dinámicamente, en tiempo de ejecución de la app, por un nombre y un conjunto de claves. Una instancia concreta de un registro tiene un identificador único y es un diccionario de parejas clave-valor con cada clave representando un campo del registro.

El valor de cada campo suele ser un tipo de datos simple como una cadena, una fecha o un número, pero es posible almacenar también bloques de datos arbitrarios (ficheros),

Es posible guardar en los valores referencias a otros registros, permitiendo definir relaciones entre registros.

Por ejemplo, en la siguiente figura se muestran dos tipos de registros con sus campos asociados. El nombre del primer tipo de registro es Artwork y el del segundo Artist.

Nota

En iCloud se dispone de dos tipos de entornos: el entorno de desarrollo y el de producción. Cuando se desarrolla la app se construyen de forma dinámica los tipos de registros, con sus identificadores y sus campos. Después se debe desplegar estos tipos de registros al entorno de producción, en donde ya no es posible modificar los tipos de registro.

Registros

Una base de datos está compuesta de registros:

Las instancias de registro son objetos de la clase CKRecord.

Cada instancia es un conjunto de parejas clave y valor (determinados por el tipo de registro) y tiene un identificador único, un objeto de la clase CKRecord.ID. Este identificador único podemos proporcionarlo en el momento de creación del registro o podemos dejar que se inicializa automáticamente, si no lo definimos.

Para crear una instancia de registro es necesario identificar el tipo de registro, definido por un String:

let artistaRecord = CKRecord(recordType: "Artista")

Si es la primera vez que se crea un registro de ese tipo, se crea el tipo de registro dinámicamente en la base de datos.

  • Una vez creado el registro se añaden valores a sus campos (que también se crean dinámicamente):
artistaRecord["artista"] = "Jonhn Lennon"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd"
artistaRecord["fechanacimiento"] = formatter.date(from: "1940/10/09")!

Datos en los registros

Es posible definir los siguientes tipos de valores que pueden haber en los campos de los registros:

  • NSString (String): Cadenas
  • NSNumber (Int, Double, ...): Números, incluidos enteros y punto flotante.
  • NSData: Bytes arbitrarios de datos (por ejemplo, la serialización binaria de un struct. No usar para almacenar ficheros binarios grandes, usar CKAsset en su lugar.
  • NSDate: Fechas
  • CLLocation: Coordenadas geográficas
  • CKReference: Referencias a otros registros para crear relaciones entre ellos.
  • CKAsset: Fichero binario.
  • Arrays de todo lo anterior

Grabación de registros

Se añaden registros a una base de datos usando la función save, a la que hay que pasar un bloque que recibe el registro salvado y un error (en caso en que no se haya podido salvar).

privateDB.save(toDoItemRecord, completionHandler: {
    (record: CKRecord?, error: Error?) in
    print("Error: \(String(describing: error))")
})

Relaciones entre registros: referencias

Es posible definir relaciones entre los registros. Por ejemplo un Artwork está relacionado con un Artist. Son similares a las claves ajenas en el tradicional modelo relacional con las que se implementan relaciones muchos-a-uno.

La clase CKReference es la utilizada para definir estas relaciones:

itemRecord["owningList"] = CKReference(record: listRecord, action: .deleteSelf)

La constante .deleteSelf indica que si el registro referenciado se borra, el propio registro también debe borrarse (borrado en cascada). La otra posible acción es .none.

Queries

Para realizar una consulta se debe utilizar la clase CKQuery para buscar objetos que cumplen una determinada condición en una base de datos.

La consulta almacena los parámetros de búsqueda, incluyendo el tipo de registros a buscar, el criterio (predicado) a aplicar, y el parámetro de ordenación que aplicar a los resultados.

El objeto de la búsqueda se usa para ejecutar una consulta en la base de datos usando el método perform

Se le pasa un manejador al que se llamará cuando se obtengan los resultados. La operación de búsqueda se restringe a los objetos de una zona (se pasa nil para la zona por defecto).

Para realizar consultas con más control sobre el número de registros devueltos, o utilizar un cursor definido por el límite de registros devueltos, hay que realizar una CKQueryOperation.

Por ejemplo, la query que devuelve todos los registros de tipo "Tarea" de la base de datos privada del usuario actual es la siguiente (se ha añadido código de ejemplo en el que se actualiza el array de tareas por hacer y se actualiza la vista de la tabla)

let privateDB = CKContainer.default().privateCloudDatabase
let query = CKQuery(recordType: "Tarea", predicate: NSPredicate(value:true))
privateDB.perform(query, inZoneWith: nil, completionHandler: {
    (results, error) in
    if error == nil {
        for result in results! {
            if let nombre = result["nombre"] {
                let toDoItem = ToDoItem(nombre: nombre as! String, publica: false)
                self.toDoItems.append(toDoItem)
            }
        }
        DispatchQueue.main.async( execute: {
            self.tableView.reloadData()
        })
    } else {
        print("Query error: \(String(describing: error))")
    }
})

Importante

Para que funcione la consulta que recupera todos los registros de un tipo hay que crear en el dashboard un índice queryable sobre el campo nativo recordName.

Otros ejemplos de predicados (consultar CKQuery y NSPredicate)

let predicate = NSPredicate(format: "nombre BEGINSWITH 'Limpiar'")
let predicate = NSPredicate(format: "favoriteColors CONTAINS 'red'")

Operaciones con registros obtenidos

Un ejemplo de código en el que borramos los registros de tipo "Tarea" cuyo nombre coincide con un nombre:

func deleteTarea(_ toDoItem: ToDoItem) {
    let query = CKQuery(recordType: "Tarea",
                        predicate: NSPredicate(format: "nombre == %@", argumentArray: [toDoItem.nombreItem]))
    let publicDB = CKContainer.default().publicCloudDatabase
    publicDB.perform(query, inZoneWith: nil, completionHandler: {
        (results, error) in
        if error == nil {
            for result in results! {
                let record: CKRecord! = result as CKRecord
                publicDB.delete(withRecordID: record.recordID, completionHandler: {
                    (recordID, error) in print("Error: \(String(describing: error))")
                })
            }
        }
    })
} 

Características sociales de CloudKit

CloudKit permite descubrirse entre ellos a usuarios que están usando nuestra app. Los usuarios podrán compartir datos de identidad (nombre de usuario y correo elctrónico) si:

  • Están en los contactos del usuario actual
  • Han dado el permiso a la app

Para que otros usuarios puedan acceder a la información del usuario actual, hay que solicitarle su aprobación llamando a la función requestApplicationPermission

Se le pasa como parámetro completionHandler el manejador de la respuesta del usuario. Recibiremos dos parámetros, el applicationPermissionStatus (constante que indica lo que ha respondido el usuario) y un objeto error que será nil si todo ha ido correctamente.

Se pueden buscar los usuarios que han dado permiso y que están en la agenda del usuario actual por su dirección de correo electrónico registrada en el Apple Id.

La función discover​All​Identities(completion​Handler:​) de CKContainer permite obtener estos usuarios

Se le pasa como parámetro completionHandler, una función que la consulta ejecutará cuando se obtengan los resultados. Tiene dos parámetros:

  • Un array de objetos CKUser​Identity que corresponde con los contactos del usuario que han autorizado conocerlos. Si no hay usuarios, el array estará vacío.

  • Un objeto error si sucede algún problema, o nil si los IDs se han obtenido correctamente.

Ejemplo de código:

let container = CKContainer.default()
print("Container: ")
print(container)
// Solicitamos permiso para que el usuario se haga descubrible
container.requestApplicationPermission(
    CKApplicationPermissions.userDiscoverability,
    completionHandler: { (permissionStatus, error) in
        print("Permiso concedido: " +
            "\(permissionStatus == CKApplicationPermissionStatus.granted)")})

// Obtenemos los usuarios de la app que han dado permiso
container.discoverAllIdentities(completionHandler: { (optUsers, error) in
    if let users = optUsers {
        for user in users {
            print(user)
            // usamos user.userRecordID para buscar
            // registros públicos de un usuario
        }
    }})

Suscripciones

Es posible hacer consultas "permanentes" que son ejecutadas en background por el servidor tras cada registro salvado.

Generan notificaciones push con los resultados.

CloudKit JS

CloudKit JS es una librería que proporciona un API JavaScript para acceder a los datos en los contenedores CloudKit.

Lanzado en WWDC 2015.

Necesita un token generado en el dashboard para acceso seguro al API en la conexión servidor-servidor.

Permite autentificarse y realizar peticiones seguras JavaScript desde una aplicación web para acceder a los datos de CloudKit.

Sistema usado por la interfaz web de las apps de Apple en la página web de iCloud (Notas, Photos, etc.)

Un ejemplo de aplicación web presentado por Apple en el que se demuestra el uso de esta librería CloudKit Catalog

Demo

App DemoCloudKit

Vamos a hacer una demostración de una sencilla app llamada DemoCloudKit. Está basada en un tutorial de la web raywenderlich.com. Se puede descargar desde este enlace.

La app tiene como bundle id es.ua.mastermoviles.DemoCloudKit.

Creación del contenedor

Debemos crear un perfil de aprovisionamiento con capacidad de acceso a CloudKit.

Para ello creamos primero el identificador del contenedor de CloudKit.

Podemos hacerlo desde la página con el listado de contenedores de CloudKit, pulsando el botón + junto a Identifiers. También se puede hacer automáticamente desde Xcode si somos administradores de la cuenta con la que estamos logeados.

Después debemos rellenar el identificador del contenedor. Como el bundle id de la app es es.ua.mastermoviles.DemoCloudKit el identificador del contenedor será:

iCloud.es.ua.mastermoviles.DemoCloudKit

Hay que tener mucho cuidado porque una vez creados los identificadores de contenedores no se pueden borrar.

Creación del App ID y asignación del contenedor

Una vez creado el identificador del contenedor, debemos crear el App ID con el permiso para iCloud y seleccionar el contenedor que vamos a usar en ese App ID.

Creamos el App ID con el bundle ID de la aplicación.

Marcamos el servicio iCloud y configuramos el contenedor.

Añadimos el contenedor recién creado, el que tiene el identificador iCloud.es.ua.mastermoviles.DemoCloudKit.

Creación del perfil de aprovisionamiento

Creamos para la demo el perfil de aprovisionamiento con el App ID anterior.

Actualización de capacidades de la app ToDoList

Escribimos como Bundle Identifier el definido por el App ID. En el caso de la demo usaremos es.ua.mastermoviles.DemoCloudKit.

Actualizamos las capacidades en Xcode añadiendo la capacidad iCloud y activando CloudKit.

Seleccionamos el equipo de la UA y automáticamente se cargará el perfil recién creado.

Dashboard

Comprobamos los permisos de los miembros del equipo de la universidad en el dashboard de iCloud. El administrador del equipo de desarrollo puede gestionar permisos para el resto de miembros. Los permisos se definen a nivel de contenedor.

Revisamos el esquema de datos y los registros. Hemos creado manualmente dos tipos de registros:

  • Establishmet
  • Note

Y hemos creado un par de registros concretos de esos tipos de registros.

Examinamos los tipos de registro, sus campos, los datos, las relaciones y las bases de datos en las que están creados.

Ejecutamos la app y mostramos el código

En la app podemos navegar por los establecimientos y vemos como se muestran sus características. También podemos comprobar las notas privadas creadas asociadas con cada establecimiento.

Probamos a modificar algún dato en el dashboard y a comprobar que se actualiza en la aplicación.

Todo el código relacionado con CloudKit está en la carpeta Model.

  • Se definen dos clases que mapean los registros: Establishment.swift y Note.swift.
  • La clase Model.swift contiene métodos para recuperar los registros de iCloud mediante queries.

Referencias