Uso de iCloud con Xamarin.iOS
La API de almacenamiento de iCloud en iOS 5 permite a las aplicaciones guardar documentos de usuario y datos específicos de la aplicación en una ubicación central y acceder a esos elementos desde todos los dispositivos del usuario.
Hay cuatro tipos de almacenamiento disponibles:
Almacenamiento de clave-valor: para compartir pequeñas cantidades de datos con la aplicación en otros dispositivos de un usuario.
Almacenamiento de UIDocument: para almacenar documentos y otros datos en la cuenta de iCloud del usuario mediante una subclase de UIDocument.
CoreData: almacenamiento de base de datos de SQLite.
Archivos y directorios individuales: para administrar muchos archivos diferentes directamente en el sistema de archivos.
En este documento se analizan los dos primeros tipos (pares Key-Value y subclases UIDocument) y cómo usar esas características en Xamarin.iOS.
Importante
Apple proporciona herramientas para ayudar a los desarrolladores a tratar correctamente el Reglamento general de protección de datos (RGPD) de la Unión Europea.
Requisitos
- La versión estable más reciente de Xamarin.iOS
- Xcode 10
- Visual Studio para Mac o Visual Studio 2019.
Preparación para el desarrollo de iCloud
Las aplicaciones deben configurarse para usar iCloud tanto en el portal de aprovisionamiento de Apple como en el propio proyecto. Antes de desarrollar para iCloud (o probar los ejemplos), siga estos pasos.
Para configurar correctamente una aplicación para acceder a iCloud:
Busque su TeamID: inicie sesión en developer.apple.com y visite el Resumen de la cuenta de desarrollador del Centro de miembros para obtener el identificador de equipo (o el identificador individual para desarrolladores individuales). Será una cadena de 10 caracteres (por ejemplo, A93A5CM278): forma parte del "identificador de contenedor".
Crear un nuevo identificador de aplicación: para crear un identificador de aplicación, siga los pasos descritos en la sección Aprovisionamiento para tecnologías de tienda de la guía de aprovisionamiento de dispositivos y asegúrese de comprobar iCloud como un servicio permitido:
Crear un nuevo perfil de aprovisionamiento: para crear un perfil de aprovisionamiento, siga los pasos descritos en la guía de aprovisionamiento de dispositivos .
Agregue el identificador de contenedor a Entitlements.plist: el formato del identificador de contenedor es . Para obtener más información, consulte la guía Trabajar con derechos.
Configurar las propiedades del proyecto: en el archivo Info.plist, asegúrese de que el identificador de agrupación coincide con el identificador de lote establecido al crear un identificador de aplicación; La firma de lotes de iOS usa un perfil de aprovisionamiento que contiene un identificador de aplicación con el App Service iCloud y el archivo de derechos personalizados seleccionado. Todo esto se puede hacer en Visual Studio panel Propiedades del proyecto.
Habilite iCloud en el dispositivo: vaya a Configuración iCloud y asegúrese de que el dispositivo ha iniciado sesión. Seleccione y active la opción Datos de documentos.
Debe usar un dispositivo para probar iCloud; no funcionará en el simulador. De hecho, realmente necesita dos o más dispositivos que han iniciado sesión con el mismo identificador de Apple para ver iCloud en acción.
Key-Value Storage
El almacenamiento de clave-valor está pensado para pequeñas cantidades de datos que un usuario podría desear conservar en todos los dispositivos, como la última página que ha visto en un libro o una revista. El almacenamiento de clave-valor no debe usarse para realizar una copia de seguridad de los datos.
Hay algunas limitaciones que se deben tener en cuenta al usar el almacenamiento de clave-valor:
Tamaño máximo de clave: los nombres de clave no pueden tener más de 64 bytes.
Tamaño máximo del valor: no se pueden almacenar más de 64 kilobytes en un solo valor.
Tamaño máximo del almacén de clave-valor para una aplicación: las aplicaciones solo pueden almacenar hasta 64 kilobytes de datos clave-valor en total. Los intentos de establecer claves más allá de ese límite producirán un error y el valor anterior se conservará.
Tipos de datos: solo se pueden almacenar tipos básicos como cadenas, números y valores booleanos.
En el ejemplo iCloudKeyValue se muestra cómo funciona. El código de ejemplo crea una clave denominada para cada dispositivo: puede establecer esta clave en un dispositivo y ver cómo el valor se propaga a otros. También crea una clave denominada "Compartido" que se puede editar en cualquier dispositivo; si edita en muchos dispositivos a la vez, iCloud decidirá qué valor "gana" (mediante una marca de tiempo en el cambio) y se propagará.
En esta captura de pantalla se muestra el ejemplo en uso. Cuando se reciben notificaciones de cambio desde iCloud, se imprimen en la vista de texto de desplazamiento en la parte inferior de la pantalla y se actualizan en los campos de entrada.
Configuración y recuperación de datos
Este código muestra cómo establecer un valor de cadena.
var store = NSUbiquitousKeyValueStore.DefaultStore;
store.SetString("testkey", "VALUE IN THE CLOUD"); // key and value
store.Synchronize();
La llamada a Synchronize garantiza que el valor solo se conserva en el almacenamiento en disco local. La sincronización con iCloud se produce en segundo plano y no se puede "forzar" mediante el código de la aplicación. Con una buena conectividad de red, la sincronización suele producirse en 5 segundos, pero si la red es deficiente (o desconectada), una actualización puede tardar mucho más tiempo.
Puede recuperar un valor con este código:
var store = NSUbiquitousKeyValueStore.DefaultStore;
display.Text = store.GetString("testkey");
El valor se recupera del almacén de datos local: este método no intenta ponerse en contacto con los servidores de iCloud para obtener el valor "más reciente". iCloud actualizará el almacén de datos local según su propia programación.
Eliminar datos
Para quitar completamente un par clave-valor, use el método Remove de la siguiente manera:
var store = NSUbiquitousKeyValueStore.DefaultStore;
store.Remove("testkey");
store.Synchronize();
Observar cambios
Una aplicación también puede recibir notificaciones cuando iCloud cambia los valores agregando un observador a NSNotificationCenter.DefaultCenter .
El código siguiente del método KeyValueViewController.cs muestra cómo escuchar esas notificaciones y crear una lista de las claves que se han cambiado:
keyValueNotification =
NSNotificationCenter.DefaultCenter.AddObserver (
NSUbiquitousKeyValueStore.DidChangeExternallyNotification, notification => {
Console.WriteLine ("Cloud notification received");
NSDictionary userInfo = notification.UserInfo;
var reasonNumber = (NSNumber)userInfo.ObjectForKey (NSUbiquitousKeyValueStore.ChangeReasonKey);
nint reason = reasonNumber.NIntValue;
var changedKeys = (NSArray)userInfo.ObjectForKey (NSUbiquitousKeyValueStore.ChangedKeysKey);
var changedKeysList = new List<string> ();
for (uint i = 0; i < changedKeys.Count; i++) {
var key = changedKeys.GetItem<NSString> (i); // resolve key to a string
changedKeysList.Add (key);
}
// now do something with the list...
});
A continuación, el código puede realizar alguna acción con la lista de claves modificadas, como actualizar una copia local de ellas o actualizar la interfaz de usuario con los nuevos valores.
Los posibles motivos de cambio son: ServerChange (0), InitialSyncChange (1) o QuotaViolationChange (2). Puede acceder al motivo y realizar un procesamiento diferente si es necesario (por ejemplo, es posible que tenga que quitar algunas claves como resultado de quotaViolationChange).
Documentos Storage
iCloud Document Storage está diseñado para administrar datos que son importantes para la aplicación (y para el usuario). Se puede usar para administrar archivos y otros datos que la aplicación necesita ejecutar, al mismo tiempo que proporciona la funcionalidad de copia de seguridad y uso compartido basada en iCloud en todos los dispositivos del usuario.
En este diagrama se muestra cómo encaja todo. Cada dispositivo tiene datos guardados en el almacenamiento local (UbiquityContainer) y el demonio de iCloud del sistema operativo se encarga de enviar y recibir datos en la nube. Todo el acceso a los archivos a UbiquityContainer debe realizarse a través de FilePresenter/FileCoordinator para evitar el acceso simultáneo. La UIDocument clase los implementa por usted; en este ejemplo se muestra cómo usar UIDocument.
En el ejemplo iCloudUIDoc se implementa una UIDocument subclase simple que contiene un único campo de texto. El texto se representa en y iCloud propaga las ediciones a otros dispositivos con un mensaje UITextView de notificación que se muestra en rojo. El código de ejemplo no trata con características de iCloud más avanzadas, como la resolución de conflictos.
Esta captura de pantalla muestra la aplicación de ejemplo: después de cambiar el texto y presionar UpdateChangeCount, el documento se sincroniza a través de iCloud con otros dispositivos.
Hay cinco partes en el ejemplo iCloudUIDoc:
Acceso a UbiquityContainer: determine si iCloud está habilitado y, si es así, la ruta de acceso al área de almacenamiento de iCloud de la aplicación.
Creación de una subclase UIDocument: cree una clase para intermediar entre el almacenamiento de iCloud y los objetos del modelo.
Búsqueda y apertura de documentos de iCloud: use y para buscar documentos de
NSPredicateiCloud y abrirlos.Mostrar documentos de iCloud: exponga las propiedades de para que pueda interactuar con los controles de interfaz de usuario.
Guardar documentos de iCloud: asegúrese de que los cambios realizados en la interfaz de usuario se conserven en el disco y en iCloud.
Todas las operaciones de iCloud se ejecutan (o deben ejecutarse) de forma asincrónica para que no se bloqueen mientras esperan a que suceda algo. Verá tres maneras diferentes de lograrlo en el ejemplo:
Subprocesos: en la llamada inicial a se realiza en otro subproceso para evitar el bloqueo del subproceso GetUrlForUbiquityContainer principal.
NotificationCenter: registro de notificaciones cuando se completan operaciones asincrónicas como .
Controladores de finalización: pasar métodos para ejecutarse al finalizar operaciones asincrónicas como .
Acceso a UbiquityContainer
El primer paso para usar iCloud Document Storage es determinar si iCloud está habilitado y, si es así, la ubicación del "contenedor ubicuo" (el directorio donde se almacenan los archivos habilitados para iCloud en el dispositivo).
Este código está en el AppDelegate.FinishedLaunching método del ejemplo.
// GetUrlForUbiquityContainer is blocking, Apple recommends background thread or your UI will freeze
ThreadPool.QueueUserWorkItem (_ => {
CheckingForiCloud = true;
Console.WriteLine ("Checking for iCloud");
var uburl = NSFileManager.DefaultManager.GetUrlForUbiquityContainer (null);
// OR instead of null you can specify "TEAMID.com.your-company.ApplicationName"
if (uburl == null) {
HasiCloud = false;
Console.WriteLine ("Can't find iCloud container, check your provisioning profile and entitlements");
InvokeOnMainThread (() => {
var alertController = UIAlertController.Create ("No \uE049 available",
"Check your Entitlements.plist, BundleId, TeamId and Provisioning Profile!", UIAlertControllerStyle.Alert);
alertController.AddAction (UIAlertAction.Create ("OK", UIAlertActionStyle.Destructive, null));
viewController.PresentViewController (alertController, false, null);
});
} else { // iCloud enabled, store the NSURL for later use
HasiCloud = true;
iCloudUrl = uburl;
Console.WriteLine ("yyy Yes iCloud! {0}", uburl.AbsoluteUrl);
}
CheckingForiCloud = false;
});
Aunque el ejemplo no lo hace, Apple recomienda llamar a GetUrlForUbiquityContainer cada vez que una aplicación llegue al primer plano.
Crear una subclase UIDocument
Todos los archivos y directorios de iCloud (es decir, todo lo almacenado en el directorio UbiquityContainer) se deben administrar mediante métodos NSFileManager, implementando el protocolo NSFilePresenter y escribiendo a través de un NSFileCoordinator. La manera más sencilla de hacer todo eso no es escribirlo usted mismo, sino la subclase UIDocument, que lo hace todo por usted.
Solo hay dos métodos que debe implementar en una subclase UIDocument para trabajar con iCloud:
LoadFromContents: pasa el NSData del contenido del archivo para que se desempaquete en la clase o es del modelo.
ContentsForType: solicitud para que proporcione la representación NSData de la clase o es del modelo para guardarla en el disco (y en la nube).
Este código de ejemplo de iCloudUIDoc\MonkeyDocument.cs muestra cómo implementar UIDocument.
public class MonkeyDocument : UIDocument
{
// the 'model', just a chunk of text in this case; must easily convert to NSData
NSString dataModel;
// model is wrapped in a nice .NET-friendly property
public string DocumentString {
get {
return dataModel.ToString ();
}
set {
dataModel = new NSString (value);
}
}
public MonkeyDocument (NSUrl url) : base (url)
{
DocumentString = "(default text)";
}
// contents supplied by iCloud to display, update local model and display (via notification)
public override bool LoadFromContents (NSObject contents, string typeName, out NSError outError)
{
outError = null;
Console.WriteLine ("LoadFromContents({0})", typeName);
if (contents != null)
dataModel = NSString.FromData ((NSData)contents, NSStringEncoding.UTF8);
// LoadFromContents called when an update occurs
NSNotificationCenter.DefaultCenter.PostNotificationName ("monkeyDocumentModified", this);
return true;
}
// return contents for iCloud to save (from the local model)
public override NSObject ContentsForType (string typeName, out NSError outError)
{
outError = null;
Console.WriteLine ("ContentsForType({0})", typeName);
Console.WriteLine ("DocumentText:{0}",dataModel);
NSData docData = dataModel.Encode (NSStringEncoding.UTF8);
return docData;
}
}
En este caso, el modelo de datos es muy sencillo: un solo campo de texto. El modelo de datos puede ser tan complejo como sea necesario, como un documento Xml o datos binarios. El rol principal de la implementación de UIDocument es traducir entre las clases de modelo y una representación de NSData que se puede guardar o cargar en el disco.
Búsqueda y apertura de documentos de iCloud
La aplicación de ejemplo solo trata con un único archivo (test.txt), por lo que el código de AppDelegate.cs crea y para buscar específicamente ese nombre NSMetadataQuery de archivo. se NSMetadataQuery ejecuta de forma asincrónica y envía una notificación cuando finaliza. DidFinishGathering el observador de notificación llama a , detiene la consulta y llama a LoadDocument, que usa el método con un controlador de finalización para intentar cargar el archivo y UIDocument.Open mostrarlo en MonkeyDocumentViewController .
string monkeyDocFilename = "test.txt";
void FindDocument ()
{
Console.WriteLine ("FindDocument");
query = new NSMetadataQuery {
SearchScopes = new NSObject [] { NSMetadataQuery.UbiquitousDocumentsScope }
};
var pred = NSPredicate.FromFormat ("%K == %@", new NSObject[] {
NSMetadataQuery.ItemFSNameKey, new NSString (MonkeyDocFilename)
});
Console.WriteLine ("Predicate:{0}", pred.PredicateFormat);
query.Predicate = pred;
NSNotificationCenter.DefaultCenter.AddObserver (
this,
new Selector ("queryDidFinishGathering:"),
NSMetadataQuery.DidFinishGatheringNotification,
query
);
query.StartQuery ();
}
[Export ("queryDidFinishGathering:")]
void DidFinishGathering (NSNotification notification)
{
Console.WriteLine ("DidFinishGathering");
var metadataQuery = (NSMetadataQuery)notification.Object;
metadataQuery.DisableUpdates ();
metadataQuery.StopQuery ();
NSNotificationCenter.DefaultCenter.RemoveObserver (this, NSMetadataQuery.DidFinishGatheringNotification, metadataQuery);
LoadDocument (metadataQuery);
}
void LoadDocument (NSMetadataQuery metadataQuery)
{
Console.WriteLine ("LoadDocument");
if (metadataQuery.ResultCount == 1) {
var item = (NSMetadataItem)metadataQuery.ResultAtIndex (0);
var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);
doc = new MonkeyDocument (url);
doc.Open (success => {
if (success) {
Console.WriteLine ("iCloud document opened");
Console.WriteLine (" -- {0}", doc.DocumentString);
viewController.DisplayDocument (doc);
} else {
Console.WriteLine ("failed to open iCloud document");
}
});
} // TODO: if no document, we need to create one
}
Mostrar documentos de iCloud
Mostrar un UIDocument no debe ser diferente de ninguna otra clase de modelo: las propiedades se muestran en los controles de interfaz de usuario, posiblemente editados por el usuario y, a continuación, se escriben de nuevo en el modelo.
En el ejemplo iCloudUIDoc\MonkeyDocumentViewController.cs muestra el texto de MonkeyDocument en . ViewDidLoad escucha la notificación enviada en el MonkeyDocument.LoadFromContents método . LoadFromContents Se llama a cuando iCloud tiene nuevos datos para el archivo, por lo que esa notificación indica que el documento se ha actualizado.
NSNotificationCenter.DefaultCenter.AddObserver (this,
new Selector ("dataReloaded:"),
new NSString ("monkeyDocumentModified"),
null
);
El controlador de notificación de código de ejemplo llama a un método para actualizar la interfaz de usuario, en este caso sin ninguna detección o resolución de conflictos.
[Export ("dataReloaded:")]
void DataReloaded (NSNotification notification)
{
doc = (MonkeyDocument)notification.Object;
// we just overwrite whatever was being typed, no conflict resolution for now
docText.Text = doc.DocumentString;
}
Guardar documentos de iCloud
Para agregar un UIDocument a iCloud, puede llamar directamente (solo para documentos nuevos) o UIDocument.Save mover un archivo existente mediante NSFileManager.DefaultManager.SetUbiquitious . El código de ejemplo crea un nuevo documento directamente en el contenedor ubicuo con este código (aquí hay dos controladores de finalización, uno para la operación y otro Save para open):
var docsFolder = Path.Combine (iCloudUrl.Path, "Documents"); // NOTE: Documents folder is user-accessible in Settings
var docPath = Path.Combine (docsFolder, MonkeyDocFilename);
var ubiq = new NSUrl (docPath, false);
var monkeyDoc = new MonkeyDocument (ubiq);
monkeyDoc.Save (monkeyDoc.FileUrl, UIDocumentSaveOperation.ForCreating, saveSuccess => {
Console.WriteLine ("Save completion:" + saveSuccess);
if (saveSuccess) {
monkeyDoc.Open (openSuccess => {
Console.WriteLine ("Open completion:" + openSuccess);
if (openSuccess) {
Console.WriteLine ("new document for iCloud");
Console.WriteLine (" == " + monkeyDoc.DocumentString);
viewController.DisplayDocument (monkeyDoc);
} else {
Console.WriteLine ("couldn't open");
}
});
} else {
Console.WriteLine ("couldn't save");
}
Los cambios posteriores en el documento no se "guardan" directamente, sino que se le dice que ha cambiado con y que programará automáticamente una operación de guardar en UIDocumentUpdateChangeCount disco:
doc.UpdateChangeCount (UIDocumentChangeKind.Done);
Administración de documentos de iCloud
Los usuarios pueden administrar documentos de iCloud en el directorio Documentos del "contenedor ubicuo" fuera de la aplicación a través de Configuración; pueden ver la lista de archivos y deslizar el dedo para eliminar. El código de la aplicación debe ser capaz de controlar la situación en la que el usuario elimina los documentos. No almacene datos internos de la aplicación en el directorio Documentos.
Los usuarios también recibirán advertencias diferentes cuando intenten quitar una aplicación habilitada para iCloud de su dispositivo para informarles del estado de los documentos de iCloud relacionados con esa aplicación.
Copia de seguridad de iCloud
Aunque la copia de seguridad en iCloud no es una característica a la que acceden directamente los desarrolladores, la forma de diseñar la aplicación puede afectar a la experiencia del usuario. Apple proporciona directrices de protección de Storage iOS para que los desarrolladores las sigan en sus aplicaciones iOS.
La consideración más importante es si la aplicación almacena archivos grandes que no son generados por el usuario (por ejemplo, una aplicación de lector de revistas que almacena más de 100 megabytes de contenido por problema). Apple prefiere que no almacene este tipo de datos en los que se realizará una copia de seguridad en iCloud y que rellene innecesariamente la cuota de iCloud del usuario.
Las aplicaciones que almacenan grandes cantidades de datos como este deben almacenarse en uno de los directorios de usuario de los que no se ha realizado una copia de seguridad (por ejemplo, Almacena en caché o tmp) o usa para aplicar una marca a esos archivos para que iCloud los ignore NSFileManager.SetSkipBackupAttribute durante las operaciones de copia de seguridad.
Resumen
En este artículo se ha presentado la nueva característica de iCloud incluida en iOS 5. Se examinaron los pasos necesarios para configurar el proyecto para usar iCloud y, a continuación, se proporcionaron ejemplos de cómo implementar características de iCloud.
En el ejemplo de almacenamiento de clave-valor se muestra cómo se puede usar iCloud para almacenar una pequeña cantidad de datos similar a la forma en que se almacenan las referencias NSUserPreferences. En el ejemplo UIDocument se mostró cómo se pueden almacenar y sincronizar datos más complejos en varios dispositivos a través de iCloud.
Por último, se ha incluido una breve explicación sobre cómo la adición de iCloud Backup debe influir en el diseño de la aplicación.






