Selector de documentos en Xamarin.iOS
El selector de documentos permite compartir documentos entre aplicaciones. Estos documentos se pueden almacenar en iCloud o en el directorio de una aplicación diferente. Los documentos se comparten a través del conjunto de extensiones de proveedor de documentos que el usuario ha instalado en su dispositivo.
Debido a la dificultad de mantener los documentos sincronizados entre las aplicaciones y la nube, introducen una cierta cantidad de complejidad necesaria.
Requisitos
Se requiere lo siguiente para completar los pasos presentados en este artículo:
- Xcode 7 e iOS 8 o posterior: las API Xcode 7 e iOS 8 o más recientes de Apple deben instalarse y configurarse en el equipo del desarrollador.
- Visual Studio o Visual Studio para Mac: se debe instalar la versión más Visual Studio para Mac de la aplicación.
- Dispositivo iOS: un dispositivo iOS que ejecuta iOS 8 o posterior.
Cambios en iCloud
Para implementar las nuevas características del selector de documentos, se han realizado los siguientes cambios en el servicio iCloud de Apple:
- El demonio de iCloud se ha reescrito completamente mediante CloudKit.
- Se ha cambiado el nombre de las características existentes de iCloud a iCloud Drive.
- Se ha agregado compatibilidad Windows sistema operativo de Microsoft a iCloud.
- Se ha agregado una carpeta de iCloud en el Mac OS Finder.
- Los dispositivos iOS pueden acceder al contenido de la carpeta Mac OS iCloud.
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.
¿Qué es un documento?
Al hacer referencia a un documento en iCloud, es una entidad independiente única y debe ser percibida como tal por el usuario. Un usuario puede desear modificar el documento o compartirlo con otros usuarios (por ejemplo, mediante el correo electrónico).
Hay varios tipos de archivos que el usuario reconocerá inmediatamente como documentos, como archivos Pages, Keynote o Numbers. Sin embargo, iCloud no se limita a este concepto. Por ejemplo, el estado de un juego (como una partida de ajedrez) se puede tratar como un documento y almacenarse en iCloud. Este archivo se podría pasar entre los dispositivos de un usuario y permitirles elegir un juego en el que lo dejaran en otro dispositivo.
Trabajar con documentos
Antes de entrar en el código necesario para usar el selector de documentos con Xamarin, en este artículo se tratan los procedimientos recomendados para trabajar con documentos de iCloud y varias de las modificaciones realizadas en las API existentes necesarias para admitir el selector de documentos.
Uso de la coordinación de archivos
Dado que un archivo se puede modificar desde varias ubicaciones diferentes, se debe usar la coordinación para evitar la pérdida de datos.
Echemos un vistazo a la ilustración anterior:
- Un dispositivo iOS mediante la coordinación de archivos crea un nuevo documento y lo guarda en la carpeta de iCloud.
- iCloud guarda el archivo modificado en la nube para su distribución en todos los dispositivos.
- Un equipo Mac asociado ve el archivo modificado en la carpeta de iCloud y usa la coordinación de archivos para copiar los cambios en el archivo.
- Un dispositivo que no usa la coordinación de archivos realiza un cambio en el archivo y lo guarda en la carpeta de iCloud. Estos cambios se replican al instante en los demás dispositivos.
Suponga que el dispositivo iOS original o el equipo Mac editó el archivo; ahora sus cambios se pierden y se sobrescriben con la versión del archivo desde el dispositivo no coordinado. Para evitar la pérdida de datos, la coordinación de archivos es una función necesaria al trabajar con documentos basados en la nube.
Uso de UIDocument
UIDocument simplifica las cosas (o en macOS) haciendo todo el trabajo pesado NSDocument para el desarrollador. Proporciona coordinación de archivos integrada con colas en segundo plano para evitar bloquear la interfaz de usuario de la aplicación.
UIDocument expone varias API de alto nivel que facilitan el esfuerzo de desarrollo de una aplicación xamarin para cualquier propósito que el desarrollador requiera.
El código siguiente crea una subclase de para implementar un documento genérico basado en texto que se puede usar para almacenar y recuperar UIDocument texto de iCloud:
using System;
using Foundation;
using UIKit;
namespace DocPicker
{
public class GenericTextDocument : UIDocument
{
#region Private Variable Storage
private NSString _dataModel;
#endregion
#region Computed Properties
public string Contents {
get { return _dataModel.ToString (); }
set { _dataModel = new NSString(value); }
}
#endregion
#region Constructors
public GenericTextDocument (NSUrl url) : base (url)
{
// Set the default document text
this.Contents = "";
}
public GenericTextDocument (NSUrl url, string contents) : base (url)
{
// Set the default document text
this.Contents = contents;
}
#endregion
#region Override Methods
public override bool LoadFromContents (NSObject contents, string typeName, out NSError outError)
{
// Clear the error state
outError = null;
// Were any contents passed to the document?
if (contents != null) {
_dataModel = NSString.FromData( (NSData)contents, NSStringEncoding.UTF8 );
}
// Inform caller that the document has been modified
RaiseDocumentModified (this);
// Return success
return true;
}
public override NSObject ContentsForType (string typeName, out NSError outError)
{
// Clear the error state
outError = null;
// Convert the contents to a NSData object and return it
NSData docData = _dataModel.Encode(NSStringEncoding.UTF8);
return docData;
}
#endregion
#region Events
public delegate void DocumentModifiedDelegate(GenericTextDocument document);
public event DocumentModifiedDelegate DocumentModified;
internal void RaiseDocumentModified(GenericTextDocument document) {
// Inform caller
if (this.DocumentModified != null) {
this.DocumentModified (document);
}
}
#endregion
}
}
La clase presentada anteriormente se usará a lo largo de este artículo al trabajar con el selector de documentos y documentos externos en GenericTextDocument una aplicación xamarin.iOS 8.
Coordinación asincrónica de archivos
iOS 8 proporciona varias nuevas características de coordinación asincrónica de archivos a través de las nuevas API de coordinación de archivos. Antes de iOS 8, todas las API de coordinación de archivos existentes eran totalmente sincrónicas. Esto significaba que el desarrollador era responsable de implementar su propia cola en segundo plano para evitar que la coordinación de archivos bloqueara la interfaz de usuario de la aplicación.
La nueva clase contiene una dirección URL que apunta al archivo y varias opciones NSFileAccessIntent para controlar el tipo de coordinación necesaria. En el código siguiente se muestra cómo mover un archivo de una ubicación a otra mediante intenciones:
// Get source options
var srcURL = NSUrl.FromFilename ("FromFile.txt");
var srcIntent = NSFileAccessIntent.CreateReadingIntent (srcURL, NSFileCoordinatorReadingOptions.ForUploading);
// Get destination options
var dstURL = NSUrl.FromFilename ("ToFile.txt");
var dstIntent = NSFileAccessIntent.CreateReadingIntent (dstURL, NSFileCoordinatorReadingOptions.ForUploading);
// Create an array
var intents = new NSFileAccessIntent[] {
srcIntent,
dstIntent
};
// Initialize a file coordination with intents
var queue = new NSOperationQueue ();
var fileCoordinator = new NSFileCoordinator ();
fileCoordinator.CoordinateAccess (intents, queue, (err) => {
// Was there an error?
if (err!=null) {
Console.WriteLine("Error: {0}",err.LocalizedDescription);
}
});
Detectar y enumerar documentos
La manera de detectar y enumerar documentos es mediante las NSMetadataQuery API existentes. En esta sección se tratarán las nuevas características agregadas a que facilitan aún más el trabajo con NSMetadataQuery documentos que antes.
Comportamiento existente
Antes de iOS 8, la recogida de cambios de archivo local era lenta, como NSMetadataQuery eliminaciones, creaciones y cambios de nombre.
En el diagrama anterior:
- Para los archivos que ya existen en el contenedor de aplicaciones, tiene registros existentes creados previamente y en cola para que estén disponibles al instante
NSMetadataQueryNSMetadatapara la aplicación. - La aplicación crea un nuevo archivo en el contenedor de aplicaciones.
- Hay un retraso antes de ver la modificación en el
NSMetadataQuerycontenedor de aplicaciones y crea el registroNSMetadatanecesario.
Debido al retraso en la creación del registro, la aplicación tenía que tener dos orígenes de datos abiertos: uno para los cambios de archivo local y otro para los cambios basados en NSMetadata la nube.
Costura
En iOS 8, NSMetadataQuery es más fácil de usar directamente con una nueva característica denominada Unión:
Uso de la unión en el diagrama anterior:
- Como antes, para los archivos que ya existen en el contenedor de aplicaciones, tiene registros existentes creados previamente
NSMetadataQueryNSMetadatay en cola. - La aplicación crea un nuevo archivo en el contenedor de aplicaciones mediante la coordinación de archivos.
- Un enlace en el contenedor de aplicaciones ve la modificación y llama
NSMetadataQuerya para crear el registroNSMetadatanecesario. - El
NSMetadataregistro se crea directamente después del archivo y está disponible para la aplicación.
Al usar Unión, la aplicación ya no tiene que abrir un origen de datos para supervisar los cambios de archivos locales y basados en la nube. Ahora la aplicación puede basarse NSMetadataQuery directamente en .
Importante
La unión solo funciona si la aplicación usa la coordinación de archivos, como se muestra en la sección anterior. Si no se usa la coordinación de archivos, las API tienen como valor predeterminado el comportamiento anterior a iOS 8 existente.
Nuevas características de metadatos de iOS 8
Se han agregado las siguientes características nuevas a NSMetadataQuery en iOS 8:
NSMetatadataQueryahora puede enumerar documentos no locales almacenados en la nube.- Se han agregado nuevas API para acceder a la información de metadatos en los documentos basados en la nube.
- Hay una nueva API que tendrá acceso a los atributos de archivo de los archivos que pueden o
NSUrl_PromisedItemsno tener su contenido disponible localmente. - Use el método para obtener información sobre un archivo determinado o use el método para obtener información sobre más de
GetPromisedItemResourceValueun archivo a laGetPromisedItemResourceValuesvez.
Se han agregado dos nuevas marcas de coordinación de archivos para trabajar con metadatos:
NSFileCoordinatorReadImmediatelyAvailableMetadataOnlyNSFileCoordinatorWriteContentIndependentMetadataOnly
Con las marcas anteriores, no es necesario que el contenido del archivo de documento esté disponible localmente para su uso.
El siguiente segmento de código muestra cómo usar para consultar la existencia de un archivo específico y compilar el archivo NSMetadataQuery si no existe:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Foundation;
using UIKit;
using ObjCRuntime;
using System.IO;
#region Static Properties
public const string TestFilename = "test.txt";
#endregion
#region Computed Properties
public bool HasiCloud { get; set; }
public bool CheckingForiCloud { get; set; }
public NSUrl iCloudUrl { get; set; }
public GenericTextDocument Document { get; set; }
public NSMetadataQuery Query { get; set; }
#endregion
#region Private Methods
private void FindDocument () {
Console.WriteLine ("Finding Document...");
// Create a new query and set it's scope
Query = new NSMetadataQuery();
Query.SearchScopes = new NSObject [] {
NSMetadataQuery.UbiquitousDocumentsScope,
NSMetadataQuery.UbiquitousDataScope,
NSMetadataQuery.AccessibleUbiquitousExternalDocumentsScope
};
// Build a predicate to locate the file by name and attach it to the query
var pred = NSPredicate.FromFormat ("%K == %@"
, new NSObject[] {
NSMetadataQuery.ItemFSNameKey
, new NSString(TestFilename)});
Query.Predicate = pred;
// Register a notification for when the query returns
NSNotificationCenter.DefaultCenter.AddObserver (this,
new Selector("queryDidFinishGathering:"), NSMetadataQuery.DidFinishGatheringNotification,
Query);
// Start looking for the file
Query.StartQuery ();
Console.WriteLine ("Querying: {0}", Query.IsGathering);
}
[Export("queryDidFinishGathering:")]
public void DidFinishGathering (NSNotification notification) {
Console.WriteLine ("Finish Gathering Documents.");
// Access the query and stop it from running
var query = (NSMetadataQuery)notification.Object;
query.DisableUpdates();
query.StopQuery();
// Release the notification
NSNotificationCenter.DefaultCenter.RemoveObserver (this
, NSMetadataQuery.DidFinishGatheringNotification
, query);
// Load the document that the query returned
LoadDocument(query);
}
private void LoadDocument (NSMetadataQuery query) {
Console.WriteLine ("Loading Document...");
// Take action based on the returned record count
switch (query.ResultCount) {
case 0:
// Create a new document
CreateNewDocument ();
break;
case 1:
// Gain access to the url and create a new document from
// that instance
NSMetadataItem item = (NSMetadataItem)query.ResultAtIndex (0);
var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);
// Load the document
OpenDocument (url);
break;
default:
// There has been an issue
Console.WriteLine ("Issue: More than one document found...");
break;
}
}
#endregion
#region Public Methods
public void OpenDocument(NSUrl url) {
Console.WriteLine ("Attempting to open: {0}", url);
Document = new GenericTextDocument (url);
// Open the document
Document.Open ( (success) => {
if (success) {
Console.WriteLine ("Document Opened");
} else
Console.WriteLine ("Failed to Open Document");
});
// Inform caller
RaiseDocumentLoaded (Document);
}
public void CreateNewDocument() {
// Create path to new file
// var docsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
var docsFolder = Path.Combine(iCloudUrl.Path, "Documents");
var docPath = Path.Combine (docsFolder, TestFilename);
var ubiq = new NSUrl (docPath, false);
// Create new document at path
Console.WriteLine ("Creating Document at:" + ubiq.AbsoluteString);
Document = new GenericTextDocument (ubiq);
// Set the default value
Document.Contents = "(default value)";
// Save document to path
Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForCreating, (saveSuccess) => {
Console.WriteLine ("Save completion:" + saveSuccess);
if (saveSuccess) {
Console.WriteLine ("Document Saved");
} else {
Console.WriteLine ("Unable to Save Document");
}
});
// Inform caller
RaiseDocumentLoaded (Document);
}
public bool SaveDocument() {
bool successful = false;
// Save document to path
Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForOverwriting, (saveSuccess) => {
Console.WriteLine ("Save completion: " + saveSuccess);
if (saveSuccess) {
Console.WriteLine ("Document Saved");
successful = true;
} else {
Console.WriteLine ("Unable to Save Document");
successful=false;
}
});
// Return results
return successful;
}
#endregion
#region Events
public delegate void DocumentLoadedDelegate(GenericTextDocument document);
public event DocumentLoadedDelegate DocumentLoaded;
internal void RaiseDocumentLoaded(GenericTextDocument document) {
// Inform caller
if (this.DocumentLoaded != null) {
this.DocumentLoaded (document);
}
}
#endregion
Miniaturas de documento
Apple considera que la mejor experiencia del usuario al enumerar documentos para una aplicación es usar vistas previas. Esto proporciona al usuario final contexto, para que pueda identificar rápidamente el documento con el que quiere trabajar.
Antes de iOS 8, mostrar las vistas previas de documentos requería una implementación personalizada. Las novedades de iOS 8 son los atributos del sistema de archivos que permiten al desarrollador trabajar rápidamente con miniaturas de documento.
Recuperación de miniaturas de documento
Al llamar a GetPromisedItemResourceValue los GetPromisedItemResourceValues métodos o NSUrl_PromisedItems , se devuelve una API , NSUrlThumbnailDictionary . La única clave actualmente en este diccionario es NSThumbnial1024X1024SizeKey y su UIImage correspondiente.
Guardar miniaturas de documento
La manera más fácil de guardar una miniatura es mediante UIDocument . Al llamar al método de y establecer la miniatura, se guardará automáticamente GetFileAttributesToWrite cuando el archivo de documento UIDocument esté. El demonio de iCloud verá este cambio y lo propagará a iCloud. En Mac OS X, el complemento De aspecto rápido genera automáticamente miniaturas para el desarrollador.
Con los conceptos básicos de trabajar con documentos basados en iCloud, junto con las modificaciones en la API existente, estamos listos para implementar el controlador de vista del selector de documentos en una aplicación móvil de Xamarin iOS 8.
Habilitación de iCloud en Xamarin
Para poder usar el selector de documentos en una aplicación xamarin.iOS, la compatibilidad con iCloud debe habilitarse tanto en la aplicación como a través de Apple.
En los pasos siguientes se explica el proceso de aprovisionamiento para iCloud.
- Cree un contenedor de iCloud.
- Cree un identificador de aplicación que contenga el identificador de iCloud App Service.
- Cree un perfil de aprovisionamiento que incluya este identificador de aplicación.
La guía Trabajar con funcionalidades le guía por los dos primeros pasos. Para crear un perfil de aprovisionamiento, siga los pasos descritos en la guía perfil de aprovisionamiento.
En los pasos siguientes se explica el proceso de configuración de la aplicación para iCloud:
Haga lo siguiente:
Abra el proyecto en Visual Studio para Mac o Visual Studio.
En la Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Opciones.
En el cuadro de diálogo Opciones, seleccione Aplicación iOS, asegúrese de que el identificador de agrupación coincide con el que se definió en id. de aplicación creado anteriormente para la aplicación.
Seleccione Firma de lote de iOS,seleccione la identidad del desarrollador y el perfil de aprovisionamiento creados anteriormente.
Haga clic en el botón Aceptar para guardar los cambios y cerrar el cuadro de diálogo.
Haga clic con el
Entitlements.plistbotón derecho enEntitlements.plistpara abrirlo en el editor.Importante
En Visual Studio puede que tenga que abrir el editor de derechos haciendo clic con el botón derecho en él, seleccionando Abrir con... y seleccionando Editor de lista de propiedades.
Active Habilitar iCloud,Documentos de iCloud, Almacenamiento de clave-valor y CloudKit.
Asegúrese de que el contenedor existe para la aplicación (como se creó anteriormente). Ejemplo:
iCloud.com.your-company.AppNameGuarde los cambios en el archivo.
Para obtener más información sobre los derechos, consulte la guía Trabajar con derechos.
Con la configuración anterior en su lugar, la aplicación ahora puede usar documentos basados en la nube y el nuevo controlador de vista del selector de documentos.
Código de instalación común
Antes de empezar a trabajar con el controlador de vista del selector de documentos, se requiere algún código de instalación estándar. Empiece modificando el archivo de la AppDelegate.cs aplicación y haga que sea parecido al siguiente:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Foundation;
using UIKit;
using ObjCRuntime;
using System.IO;
namespace DocPicker
{
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
#region Static Properties
public const string TestFilename = "test.txt";
#endregion
#region Computed Properties
public override UIWindow Window { get; set; }
public bool HasiCloud { get; set; }
public bool CheckingForiCloud { get; set; }
public NSUrl iCloudUrl { get; set; }
public GenericTextDocument Document { get; set; }
public NSMetadataQuery Query { get; set; }
public NSData Bookmark { get; set; }
#endregion
#region Private Methods
private void FindDocument () {
Console.WriteLine ("Finding Document...");
// Create a new query and set it's scope
Query = new NSMetadataQuery();
Query.SearchScopes = new NSObject [] {
NSMetadataQuery.UbiquitousDocumentsScope,
NSMetadataQuery.UbiquitousDataScope,
NSMetadataQuery.AccessibleUbiquitousExternalDocumentsScope
};
// Build a predicate to locate the file by name and attach it to the query
var pred = NSPredicate.FromFormat ("%K == %@",
new NSObject[] {NSMetadataQuery.ItemFSNameKey
, new NSString(TestFilename)});
Query.Predicate = pred;
// Register a notification for when the query returns
NSNotificationCenter.DefaultCenter.AddObserver (this
, new Selector("queryDidFinishGathering:")
, NSMetadataQuery.DidFinishGatheringNotification
, Query);
// Start looking for the file
Query.StartQuery ();
Console.WriteLine ("Querying: {0}", Query.IsGathering);
}
[Export("queryDidFinishGathering:")]
public void DidFinishGathering (NSNotification notification) {
Console.WriteLine ("Finish Gathering Documents.");
// Access the query and stop it from running
var query = (NSMetadataQuery)notification.Object;
query.DisableUpdates();
query.StopQuery();
// Release the notification
NSNotificationCenter.DefaultCenter.RemoveObserver (this
, NSMetadataQuery.DidFinishGatheringNotification
, query);
// Load the document that the query returned
LoadDocument(query);
}
private void LoadDocument (NSMetadataQuery query) {
Console.WriteLine ("Loading Document...");
// Take action based on the returned record count
switch (query.ResultCount) {
case 0:
// Create a new document
CreateNewDocument ();
break;
case 1:
// Gain access to the url and create a new document from
// that instance
NSMetadataItem item = (NSMetadataItem)query.ResultAtIndex (0);
var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);
// Load the document
OpenDocument (url);
break;
default:
// There has been an issue
Console.WriteLine ("Issue: More than one document found...");
break;
}
}
#endregion
#region Public Methods
public void OpenDocument(NSUrl url) {
Console.WriteLine ("Attempting to open: {0}", url);
Document = new GenericTextDocument (url);
// Open the document
Document.Open ( (success) => {
if (success) {
Console.WriteLine ("Document Opened");
} else
Console.WriteLine ("Failed to Open Document");
});
// Inform caller
RaiseDocumentLoaded (Document);
}
public void CreateNewDocument() {
// Create path to new file
// var docsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
var docsFolder = Path.Combine(iCloudUrl.Path, "Documents");
var docPath = Path.Combine (docsFolder, TestFilename);
var ubiq = new NSUrl (docPath, false);
// Create new document at path
Console.WriteLine ("Creating Document at:" + ubiq.AbsoluteString);
Document = new GenericTextDocument (ubiq);
// Set the default value
Document.Contents = "(default value)";
// Save document to path
Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForCreating, (saveSuccess) => {
Console.WriteLine ("Save completion:" + saveSuccess);
if (saveSuccess) {
Console.WriteLine ("Document Saved");
} else {
Console.WriteLine ("Unable to Save Document");
}
});
// Inform caller
RaiseDocumentLoaded (Document);
}
/// <summary>
/// Saves the document.
/// </summary>
/// <returns><c>true</c>, if document was saved, <c>false</c> otherwise.</returns>
public bool SaveDocument() {
bool successful = false;
// Save document to path
Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForOverwriting, (saveSuccess) => {
Console.WriteLine ("Save completion: " + saveSuccess);
if (saveSuccess) {
Console.WriteLine ("Document Saved");
successful = true;
} else {
Console.WriteLine ("Unable to Save Document");
successful=false;
}
});
// Return results
return successful;
}
#endregion
#region Override Methods
public override void FinishedLaunching (UIApplication application)
{
// Start a new thread to check and see if the user has iCloud
// enabled.
new Thread(new ThreadStart(() => {
// Inform caller that we are checking for iCloud
CheckingForiCloud = true;
// Checks to see if the user of this device has iCloud
// enabled
var uburl = NSFileManager.DefaultManager.GetUrlForUbiquityContainer(null);
// Connected to iCloud?
if (uburl == null)
{
// No, inform caller
HasiCloud = false;
iCloudUrl =null;
Console.WriteLine("Unable to connect to iCloud");
InvokeOnMainThread(()=>{
var okAlertController = UIAlertController.Create ("iCloud Not Available", "Developer, please check your Entitlements.plist, Bundle ID and Provisioning Profiles.", UIAlertControllerStyle.Alert);
okAlertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, null));
Window.RootViewController.PresentViewController (okAlertController, true, null);
});
}
else
{
// Yes, inform caller and save location the Application Container
HasiCloud = true;
iCloudUrl = uburl;
Console.WriteLine("Connected to iCloud");
// If we have made the connection with iCloud, start looking for documents
InvokeOnMainThread(()=>{
// Search for the default document
FindDocument ();
});
}
// Inform caller that we are no longer looking for iCloud
CheckingForiCloud = false;
})).Start();
}
// This method is invoked when the application is about to move from active to inactive state.
// OpenGL applications should use this method to pause.
public override void OnResignActivation (UIApplication application)
{
}
// This method should be used to release shared resources and it should store the application state.
// If your application supports background execution this method is called instead of WillTerminate
// when the user quits.
public override void DidEnterBackground (UIApplication application)
{
// Trap all errors
try {
// Values to include in the bookmark packet
var resources = new string[] {
NSUrl.FileSecurityKey,
NSUrl.ContentModificationDateKey,
NSUrl.FileResourceIdentifierKey,
NSUrl.FileResourceTypeKey,
NSUrl.LocalizedNameKey
};
// Create the bookmark
NSError err;
Bookmark = Document.FileUrl.CreateBookmarkData (NSUrlBookmarkCreationOptions.WithSecurityScope, resources, iCloudUrl, out err);
// Was there an error?
if (err != null) {
// Yes, report it
Console.WriteLine ("Error Creating Bookmark: {0}", err.LocalizedDescription);
}
}
catch (Exception e) {
// Report error
Console.WriteLine ("Error: {0}", e.Message);
}
}
// This method is called as part of the transition from background to active state.
public override void WillEnterForeground (UIApplication application)
{
// Is there any bookmark data?
if (Bookmark != null) {
// Trap all errors
try {
// Yes, attempt to restore it
bool isBookmarkStale;
NSError err;
var srcUrl = new NSUrl (Bookmark, NSUrlBookmarkResolutionOptions.WithSecurityScope, iCloudUrl, out isBookmarkStale, out err);
// Was there an error?
if (err != null) {
// Yes, report it
Console.WriteLine ("Error Loading Bookmark: {0}", err.LocalizedDescription);
} else {
// Load document from bookmark
OpenDocument (srcUrl);
}
}
catch (Exception e) {
// Report error
Console.WriteLine ("Error: {0}", e.Message);
}
}
}
// This method is called when the application is about to terminate. Save data, if needed.
public override void WillTerminate (UIApplication application)
{
}
#endregion
#region Events
public delegate void DocumentLoadedDelegate(GenericTextDocument document);
public event DocumentLoadedDelegate DocumentLoaded;
internal void RaiseDocumentLoaded(GenericTextDocument document) {
// Inform caller
if (this.DocumentLoaded != null) {
this.DocumentLoaded (document);
}
}
#endregion
}
}
Importante
El código anterior incluye el código de la sección De descubrimiento y enumeración de documentos anterior. Se presenta aquí en su totalidad, como aparecería en una aplicación real. Por motivos de simplicidad, este ejemplo funciona solo con un único archivo codificado de forma test.txt hard-coded ( ).
El código anterior expone varios accesos directos de iCloud Drive para facilitar su trabajo en el resto de la aplicación.
A continuación, agregue el código siguiente a cualquier contenedor de vistas o vistas que use el selector de documentos o trabaje con documentos basados en la nube:
using CloudKit;
...
#region Computed Properties
/// <summary>
/// Returns the delegate of the current running application
/// </summary>
/// <value>The this app.</value>
public AppDelegate ThisApp {
get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}
#endregion
Esto agrega un acceso directo para obtener acceso a y acceder a AppDelegate los accesos directos de iCloud creados anteriormente.
Con este código en su lugar, echemos un vistazo a la implementación del controlador de vista del selector de documentos en una aplicación xamarin iOS 8.
Uso del controlador de vista del selector de documentos
Antes de iOS 8, era muy difícil acceder a documentos desde otra aplicación porque no había ninguna manera de detectar documentos fuera de la aplicación desde dentro de la aplicación.
Comportamiento existente
Echemos un vistazo al acceso a un documento externo anterior a iOS 8:
- En primer lugar, el usuario tendría que abrir la aplicación que creó originalmente el documento.
- El documento está seleccionado y
UIDocumentInteractionControllerse usa para enviar el documento a la nueva aplicación. - Por último, se coloca una copia del documento original en el contenedor de la nueva aplicación.
Desde allí, el documento está disponible para que la segunda aplicación se abra y edite.
Detectar documentos fuera del contenedor de una aplicación
En iOS 8, una aplicación puede acceder a documentos fuera de su propio contenedor de aplicaciones con facilidad:
Con el nuevo selector de documentos de iCloud ( ), una aplicación de iOS puede detectar y acceder directamente UIDocumentPickerViewController fuera de su contenedor de aplicaciones. proporciona un mecanismo para que el usuario conceda acceso a esos documentos detectados y editarlos mediante UIDocumentPickerViewController permisos.
Una aplicación debe participar para que sus documentos se muestren en el selector de documentos de iCloud y estén disponibles para que otras aplicaciones las detecten y trabajen con ellas. Para que una aplicación de Xamarin iOS 8 comparta su contenedor de aplicaciones, edite el archivo en un editor de texto estándar y agregue las dos líneas siguientes a la parte inferior del diccionario (entre las Info.plist<dict>...</dict> etiquetas):
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
proporciona UIDocumentPickerViewController una nueva interfaz de usuario excelente que permite al usuario elegir documentos. Para mostrar el controlador de vista del selector de documentos en una aplicación de Xamarin iOS 8, haga lo siguiente:
using MobileCoreServices;
...
// Allow the Document picker to select a range of document types
var allowedUTIs = new string[] {
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image
};
// Display the picker
//var picker = new UIDocumentPickerViewController (allowedUTIs, UIDocumentPickerMode.Open);
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Open);
pickerMenu.DidPickDocumentPicker += (sender, args) => {
// Wireup Document Picker
args.DocumentPicker.DidPickDocument += (sndr, pArgs) => {
// IMPORTANT! You must lock the security scope before you can
// access this file
var securityEnabled = pArgs.Url.StartAccessingSecurityScopedResource();
// Open the document
ThisApp.OpenDocument(pArgs.Url);
// IMPORTANT! You must release the security lock established
// above.
pArgs.Url.StopAccessingSecurityScopedResource();
};
// Display the document picker
PresentViewController(args.DocumentPicker,true,null);
};
pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(pickerMenu,true,null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover!=null) {
presentationPopover.SourceView = this.View;
presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
presentationPopover.SourceRect = ((UIButton)s).Frame;
}
Importante
El desarrollador debe llamar al StartAccessingSecurityScopedResource método de para poder acceder a un documento NSUrl externo. Se StopAccessingSecurityScopedResource debe llamar al método para liberar el bloqueo de seguridad en cuanto se haya cargado el documento.
Salida de ejemplo
Este es un ejemplo de cómo el código anterior mostraría un selector de documentos cuando se ejecuta en un iPhone dispositivo:
El usuario inicia la aplicación y se muestra la interfaz principal:
El usuario pulsa el botón Acción en la parte superior de la pantalla y se le pide que seleccione un proveedor de documentos en la lista de proveedores disponibles:
El controlador de vista del selector de documentos se muestra para el proveedor de documentos seleccionado:
El usuario pulsa en una carpeta de documentos para mostrar su contenido:
El usuario selecciona un documento y se cierra el selector de documentos.
Se vuelve a mostrar la interfaz principal, el documento se carga desde el contenedor externo y se muestra su contenido.
La presentación real del controlador de vista del selector de documentos depende de los proveedores de documentos que el usuario haya instalado en el dispositivo y del modo selector de documentos que se haya implementado. En el ejemplo anterior se usa el modo abierto, los demás tipos de modo se tratarán con detalle a continuación.
Administración de documentos externos
Como se ha descrito anteriormente, antes de iOS 8, una aplicación solo podía acceder a documentos que formaban parte de su contenedor de aplicaciones. En iOS 8, una aplicación puede acceder a documentos desde orígenes externos:
Cuando el usuario selecciona un documento de un origen externo, se escribe un documento de referencia en el contenedor de aplicaciones que apunta al documento original.
Para ayudar a agregar esta nueva capacidad a las aplicaciones existentes, se han agregado varias características nuevas a la NSMetadataQuery API. Normalmente, una aplicación usa el ámbito de documento ubicuo para enumerar los documentos que se encuentran dentro de su contenedor de aplicaciones. Con este ámbito, solo se seguirán mostrando los documentos del contenedor de aplicaciones.
Con el nuevo ámbito de documento externo ubicuo se devolverán los documentos que se encuentran fuera del contenedor de aplicaciones y se devolverán los metadatos para ellos. apuntará NSMetadataItemUrlKey a la dirección URL donde se encuentra realmente el documento.
A veces, una aplicación no quiere trabajar con los documentos a los que apunta la referencia. En su lugar, la aplicación quiere trabajar directamente con el documento de referencia. Por ejemplo, es posible que la aplicación quiera mostrar el documento en la carpeta de la aplicación en la interfaz de usuario o permitir que el usuario mueva las referencias dentro de una carpeta.
En iOS 8, se ha proporcionado un nuevo para NSMetadataItemUrlInLocalContainerKey acceder directamente al documento de referencia. Esta clave apunta a la referencia real al documento externo en un contenedor de aplicaciones.
se usa para probar si un documento NSMetadataUbiquitousItemIsExternalDocumentKey es o no externo al contenedor de una aplicación. se usa para tener acceso al nombre del contenedor que contiene NSMetadataUbiquitousItemContainerDisplayNameKey la copia original de un documento externo.
Por qué se requieren referencias de documento
La razón principal por la que iOS 8 usa referencias para acceder a documentos externos es la seguridad. Ninguna aplicación tiene acceso al contenedor de ninguna otra aplicación. Solo el selector de documentos puede hacerlo, ya que se está quedando sin procesar y tiene acceso a todo el sistema.
La única manera de llegar a un documento fuera del contenedor de aplicaciones es mediante el selector de documentos y si la dirección URL devuelta por el selector es Ámbito de seguridad. La dirección URL con ámbito de seguridad contiene información suficiente para seleccionar el documento junto con los derechos con ámbito necesarios para conceder a una aplicación acceso al documento.
Es importante tener en cuenta que si la dirección URL con ámbito de seguridad se serializa en una cadena y, a continuación, se deserializó, se perdería la información de seguridad y el archivo sería inaccesible desde la dirección URL. La característica Referencia de documento proporciona un mecanismo para volver a los archivos a los que apuntan estas direcciones URL.
Por lo tanto, si la aplicación adquiere un de uno de los documentos de referencia, ya tiene el ámbito de seguridad asociado y se puede usar NSUrl para acceder al archivo. Por esta razón, se recomienda encarecidamente que el desarrollador use porque controla UIDocument toda esta información y los procesos para ellos.
Utilizar marcadores
No siempre es factible enumerar los documentos de una aplicación para volver a un documento específico, por ejemplo, al realizar la restauración del estado. iOS 8 proporciona un mecanismo para crear marcadores que tienen como destino directamente un documento determinado.
El código siguiente creará un marcador a partir de UIDocument una propiedad FileUrl de :
// Trap all errors
try {
// Values to include in the bookmark packet
var resources = new string[] {
NSUrl.FileSecurityKey,
NSUrl.ContentModificationDateKey,
NSUrl.FileResourceIdentifierKey,
NSUrl.FileResourceTypeKey,
NSUrl.LocalizedNameKey
};
// Create the bookmark
NSError err;
Bookmark = Document.FileUrl.CreateBookmarkData (NSUrlBookmarkCreationOptions.WithSecurityScope, resources, iCloudUrl, out err);
// Was there an error?
if (err != null) {
// Yes, report it
Console.WriteLine ("Error Creating Bookmark: {0}", err.LocalizedDescription);
}
}
catch (Exception e) {
// Report error
Console.WriteLine ("Error: {0}", e.Message);
}
Bookmark API existente se usa para crear un marcador en un existente que se puede guardar y cargar para proporcionar acceso NSUrl directo a un archivo externo. El código siguiente restaurará un marcador que se creó anteriormente:
if (Bookmark != null) {
// Trap all errors
try {
// Yes, attempt to restore it
bool isBookmarkStale;
NSError err;
var srcUrl = new NSUrl (Bookmark, NSUrlBookmarkResolutionOptions.WithSecurityScope, iCloudUrl, out isBookmarkStale, out err);
// Was there an error?
if (err != null) {
// Yes, report it
Console.WriteLine ("Error Loading Bookmark: {0}", err.LocalizedDescription);
} else {
// Load document from bookmark
OpenDocument (srcUrl);
}
}
catch (Exception e) {
// Report error
Console.WriteLine ("Error: {0}", e.Message);
}
}
Open frente al modo de importación y el selector de documentos
El controlador de vista del selector de documentos presenta dos modos de funcionamiento diferentes:
Modo abierto: en este modo, cuando el usuario selecciona y el documento externo, el selector de documentos creará un marcador con ámbito de seguridad en el contenedor de aplicaciones.
Modo de importación: en este modo, cuando el usuario selecciona y el documento externo, el selector de documentos no creará un marcador, sino que copiará el archivo en una ubicación temporal y proporcionará a la aplicación acceso al documento en esta ubicación:

Una vez que la aplicación finaliza por cualquier motivo, la ubicación temporal se vacía y se quita el archivo. Si la aplicación necesita mantener el acceso al archivo, debe realizar una copia y colocarla en su contenedor de aplicaciones.
El modo abierto es útil cuando la aplicación desea colaborar con otra aplicación y compartir los cambios realizados en el documento con esa aplicación. El modo de importación se usa cuando la aplicación no desea compartir sus modificaciones en un documento con otras aplicaciones.
Convertir un documento en externo
Como se indicó anteriormente, una aplicación iOS 8 no tiene acceso a contenedores fuera de su propio contenedor de aplicaciones. La aplicación puede escribir en su propio contenedor localmente o en una ubicación temporal y, a continuación, usar un modo de documento especial para mover el documento resultante fuera del contenedor de aplicaciones a una ubicación elegida por el usuario.
Para mover un documento a una ubicación externa, haga lo siguiente:
- En primer lugar, cree un nuevo documento en una ubicación local o temporal.
- Cree un
NSUrlobjeto que apunta al nuevo documento. - Abra un nuevo controlador de vista del selector de documentos y páselo
NSUrlcon el modo deMoveToService. - Una vez que el usuario elige una nueva ubicación, el documento se mueve de su ubicación actual a la nueva ubicación.
- Se escribirá un documento de referencia en el contenedor de aplicaciones de la aplicación para que la aplicación de creación pueda acceder al archivo.
El código siguiente se puede usar para mover un documento a una ubicación externa: var picker = new UIDocumentPickerViewController (srcURL, UIDocumentPickerMode.MoveToService);
El documento de referencia devuelto por el proceso anterior es exactamente el mismo que el creado por el modo abierto del selector de documentos. Sin embargo, hay ocasiones en las que la aplicación podría desear mover un documento sin mantener una referencia a él.
Para mover un documento sin generar una referencia, use el ExportToService modo . Ejemplo: var picker = new UIDocumentPickerViewController (srcURL, UIDocumentPickerMode.ExportToService);
Cuando se usa el modo , el documento se copia en el contenedor externo y ExportToService la copia existente se deja en su ubicación original.
Extensiones de proveedor de documentos
Con iOS 8, Apple quiere que el usuario final pueda acceder a cualquiera de sus documentos basados en la nube, independientemente de dónde existan realmente. Para lograr este objetivo, iOS 8 proporciona un nuevo mecanismo de extensión del proveedor de documentos.
¿Qué es una extensión de proveedor de documentos?
En pocas palabras, una extensión de proveedor de documentos es una manera para que un desarrollador, o un tercero, proporcione a una aplicación almacenamiento de documentos alternativo al que se pueda acceder exactamente de la misma manera que la ubicación de almacenamiento de iCloud existente.
El usuario puede seleccionar una de estas ubicaciones de almacenamiento alternativas en el Selector de documentos y puede usar exactamente los mismos modos de acceso (Abrir, Importar, Mover o Exportar) para trabajar con archivos en esa ubicación.
Esto se implementa mediante dos extensiones diferentes:
- Extensión del selector de documentos: proporciona una subclase que proporciona una interfaz gráfica para que el usuario elija un documento de una ubicación de almacenamiento alternativa. Esta subclase se mostrará como parte del controlador de vistas del selector de documentos.
- Extensión de proporcionar archivo: se trata de una extensión que no es de interfaz de usuario que se ocupa de proporcionar realmente el contenido de los archivos. Estas extensiones se proporcionan a través de la coordinación de archivos (
NSFileCoordinator). Este es otro caso importante en el que se requiere la coordinación de archivos.
En el diagrama siguiente se muestra el flujo de datos típico al trabajar con extensiones de proveedor de documentos:
Se produce el siguiente proceso:
- La aplicación presenta un controlador de selector de documentos para permitir al usuario seleccionar un archivo con el que trabajar.
- El usuario selecciona una ubicación de archivo alternativa y se llama a la extensión
UIViewControllerpersonalizada para mostrar la interfaz de usuario. - El usuario selecciona un archivo de esta ubicación y la dirección URL se vuelve a pasar al selector de documentos.
- El selector de documentos selecciona la dirección URL del archivo y la devuelve a la aplicación para que el usuario trabaje.
- La dirección URL se pasa al Coordinador de archivos para devolver el contenido de los archivos a la aplicación.
- El Coordinador de archivos llama a la extensión de proveedor de archivos personalizada para recuperar el archivo.
- El contenido del archivo se devuelve al Coordinador de archivos.
- El contenido del archivo se devuelve a la aplicación.
Seguridad y marcadores
En esta sección se echará un vistazo rápido a cómo funciona la seguridad y el acceso persistente a los archivos a través de marcadores con las extensiones del proveedor de documentos. A diferencia del proveedor de documentos de iCloud, que guarda automáticamente seguridad y marcadores en el contenedor de aplicaciones, las extensiones del proveedor de documentos no lo hacen porque no forman parte del sistema de referencia de documentos.
Por ejemplo: en una configuración de Enterprise que proporciona su propio almacén de datos seguro para toda la empresa, los administradores no quieren que los servidores públicos de iCloud accedan o procese información corporativa confidencial. Por lo tanto, no se puede usar el sistema de referencia de documentos integrado.
El sistema bookmark todavía se puede usar y es responsabilidad de la extensión del proveedor de archivos procesar correctamente una dirección URL marcada y devolver el contenido del documento al que apunta.
Por motivos de seguridad, iOS 8 tiene una capa de aislamiento que conserva la información sobre qué aplicación tiene acceso a qué identificador dentro de qué proveedor de archivos. Debe tenerse en cuenta que todo el acceso a archivos se controla mediante esta capa de aislamiento.
En el diagrama siguiente se muestra el flujo de datos al trabajar con marcadores y una extensión de proveedor de documentos:
Se produce el siguiente proceso:
- La aplicación está a punto de entrar en segundo plano y debe conservar su estado. Llama a
NSUrlpara crear un marcador en un archivo en almacenamiento alternativo. NSUrlllama a la extensión del proveedor de archivos para obtener una dirección URL persistente al documento.- La extensión del proveedor de archivos devuelve la dirección URL como una cadena a
NSUrl. - Agrupa
NSUrlla dirección URL en un marcador y la devuelve a la aplicación. - Cuando la aplicación no está en segundo plano y necesita restaurar el estado, pasa el marcador a
NSUrl. NSUrlllama a la extensión del proveedor de archivos con la dirección URL del archivo.- El proveedor de extensiones de archivo tiene acceso al archivo y devuelve la ubicación del archivo a
NSUrl. - La ubicación del archivo se incluye con información de seguridad y se devuelve a la aplicación.
Desde aquí, la aplicación puede acceder al archivo y trabajar con él de la forma habitual.
Escribir archivos
En esta sección se echará un vistazo rápido a cómo funciona la escritura de archivos en una ubicación alternativa con una extensión de proveedor de documentos. La aplicación iOS usará la coordinación de archivos para guardar información en el disco dentro del contenedor de aplicaciones. Poco después de que el archivo se haya escrito correctamente, se notificará el cambio a la extensión del proveedor de archivos.
En este momento, la extensión del proveedor de archivos puede empezar a cargar el archivo en la ubicación alternativa (o marcar el archivo como desaprobado y requerir carga).
Crear nuevas extensiones de proveedor de documentos
La creación de nuevas extensiones de proveedor de documentos está fuera del ámbito de este artículo introductorio. Esta información se proporciona aquí para mostrar que, en función de las extensiones que un usuario ha cargado en su dispositivo iOS, una aplicación puede tener acceso a ubicaciones de almacenamiento de documentos fuera de la ubicación de iCloud proporcionada por Apple.
El desarrollador debe tener en cuenta este hecho al usar el selector de documentos y trabajar con documentos externos. No deben asumir que el documento se hospeda en iCloud.
Para obtener más información sobre cómo crear Storage proveedor de aplicaciones o la extensión del selector de documentos, consulte el documento Introducción a las extensiones de aplicación.
Migración a iCloud Drive
En iOS 8, los usuarios pueden optar por seguir usando el sistema de documentos de iCloud existente que se usa en iOS 7 (y sistemas anteriores) o pueden optar por migrar los documentos existentes al nuevo mecanismo de iCloud Drive.
En Mac OS X Yosemite, Apple no proporciona la compatibilidad con versiones anteriores, por lo que todos los documentos deben migrarse a iCloud Drive o ya no se actualizarán entre dispositivos.
Una vez que la cuenta de un usuario se haya migrado a iCloud Drive, solo los dispositivos que usan iCloud Drive podrán propagar los cambios a documentos en esos dispositivos.
Importante
Los desarrolladores deben tener en cuenta que las nuevas características que se tratan en este artículo solo están disponibles si la cuenta del usuario se ha migrado a iCloud Drive.
Resumen
En este artículo se han abordado los cambios en las API de iCloud existentes necesarias para admitir iCloud Drive y el nuevo controlador de vista del selector de documentos. Ha abordado la coordinación de archivos y por qué es importante al trabajar con documentos basados en la nube. Ha cubierto la configuración necesaria para habilitar documentos basados en la nube en una aplicación xamarin.iOS y ha dado un vistazo introductorio al trabajo con documentos fuera del contenedor de aplicaciones de una aplicación mediante el controlador de vista del selector de documentos.
Además, en este artículo se han abordado brevemente las extensiones del proveedor de documentos y por qué el desarrollador debe tenerlas en cuenta al escribir aplicaciones que puedan controlar documentos basados en la nube.











