Entrega en Xamarin.iOS

En este artículo se explica cómo trabajar con Handoff en una aplicación de Xamarin.iOS para transferir actividades de usuario entre aplicaciones que se ejecutan en otros dispositivos del usuario.

Apple introdujo Handoff en iOS 8 y OS X Yosemite (10.10) para proporcionar un mecanismo común para que el usuario transfiera actividades iniciadas en uno de sus dispositivos, a otro dispositivo que ejecuta la misma aplicación u otra aplicación que admita la misma actividad.

An example of performing a Handoff operation

En este artículo se examinará rápidamente la habilitación del uso compartido de actividades en una aplicación de Xamarin.iOS y se tratará el marco handoff en detalle:

Acerca de la entrega

Apple introdujo Handoff (también conocido como Continuidad) en iOS 8 y OS X Yosemite (10.10) como una manera de que el usuario inicie una actividad en uno de sus dispositivos (ya sea iOS o Mac) y continúe con esa misma actividad en otro de sus dispositivos (según la identificación por la cuenta de iCloud del usuario).

Handoff se ha expandido en iOS 9 para admitir también nuevas funcionalidades de búsqueda mejoradas. Para más información, vea la documentación sobre Mejoras de búsqueda.

Por ejemplo, el usuario puede iniciar un correo electrónico en su i Teléfono y continuar sin problemas el correo electrónico en su Mac, con toda la misma información de mensaje rellenada y el cursor en la misma ubicación que la dejó en iOS.

Cualquiera de las aplicaciones que comparten el mismo identificador de equipo es apta para usar Handoff para continuar las actividades de usuario en las aplicaciones siempre que estas aplicaciones se entreguen a través de iTunes App Store o estén firmadas por un desarrollador registrado (para aplicaciones Mac, Enterprise o Ad Hoc).

Las NSDocument aplicaciones basadas o UIDocument basadas automáticamente tienen compatibilidad integrada con Handoff y requieren cambios mínimos para admitir Handoff.

Actividades continuas del usuario

La NSUserActivity clase (junto con algunos pequeños cambios UIKit en y AppKit) proporciona compatibilidad para definir la actividad de un usuario que puede continuar en otro de los dispositivos del usuario.

Para que una actividad se pase a otro de los dispositivos del usuario, debe encapsularse en una instancia NSUserActivityde , marcada como actividad actual, tener establecido la carga útil (los datos usados para realizar la continuación) y la actividad debe transmitirse a ese dispositivo.

La entrega pasa el mínimo mínimo de información para definir la actividad que se va a continuar, con paquetes de datos más grandes que se sincronizan a través de iCloud.

En el dispositivo receptor, el usuario recibirá una notificación de que una actividad está disponible para la continuación. Si el usuario decide continuar con la actividad en el nuevo dispositivo, se inicia la aplicación especificada (si aún no se está ejecutando) y la carga de NSUserActivity se usa para reiniciar la actividad.

An overview of Continuing User Activities

Solo las aplicaciones que comparten el mismo identificador de equipo de desarrollador y responden a un tipo de actividad determinado son aptas para la continuación. Una aplicación define los tipos de actividad que admite en la NSUserActivityTypes clave de su archivo Info.plist . Dado esto, un dispositivo continuo elige la aplicación para realizar la continuación en función del identificador de equipo, el tipo de actividad y, opcionalmente, el título de la actividad.

La aplicación receptora usa información del NSUserActivitydiccionario del UserInfo para configurar su interfaz de usuario y restaurar el estado de la actividad dada para que la transición aparezca sin problemas al usuario final.

Si la continuación requiere más información de la que se puede enviar de forma eficaz a través de , NSUserActivityla aplicación de reanudación puede enviar una llamada a la aplicación de origen y establecer uno o más flujos para transmitir los datos necesarios. Por ejemplo, si la actividad editaba un documento de texto grande con varias imágenes, el streaming sería necesario para transferir la información necesaria para continuar la actividad en el dispositivo receptor. Para obtener más información, consulte la sección De continuación auxiliar Secuencias siguiente.

Como se indicó anteriormente, NSDocument o UIDocument las aplicaciones basadas automáticamente tienen compatibilidad integrada con Handoff. Para obtener más información, consulte la sección Compatibilidad con entrega en aplicaciones basadas en documentos a continuación.

La clase NSUserActivity

La NSUserActivity clase es el objeto principal de un intercambio de entrega y se usa para encapsular el estado de una actividad de usuario que está disponible para la continuación. Una aplicación creará una instancia de una copia de NSUserActivity para cualquier actividad que admita y desea continuar en otro dispositivo. Por ejemplo, el editor de documentos crearía una actividad para cada documento abierto actualmente. Sin embargo, solo el documento más frontal (que se muestra en la ventana o la pestaña) es la actividad actual y está disponible para la continuación.

Una instancia de NSUserActivity se identifica mediante sus ActivityType propiedades y Title . La UserInfo propiedad dictionary se usa para llevar información sobre el estado de la actividad. Establezca la NeedsSave propiedad true en si desea cargar la información de estado diferida a través NSUserActivitydel delegado de . Use el AddUserInfoEntries método para combinar nuevos datos de otros clientes en el UserInfo diccionario según sea necesario para conservar el estado de la actividad.

La clase NSUserActivityDelegate

NSUserActivityDelegate se usa para mantener la información en un NSUserActivityUserInfo diccionario actualizado y sincronizado con el estado actual de la actividad. Cuando el sistema necesita que se actualice la información de la actividad (por ejemplo, antes de la continuación en otro dispositivo), llama al UserActivityWillSave método del delegado.

Tendrá que implementar el UserActivityWillSave método y realizar cualquier cambio en NSUserActivity (como UserInfo, Title, etc.) para asegurarse de que sigue reflejando el estado de la actividad actual. Cuando el sistema llama al UserActivityWillSave método , se borrará la NeedsSave marca. Si modifica cualquiera de las propiedades de datos de la actividad, tendrá que establecer en NeedsSavetrue de nuevo.

En lugar de usar el UserActivityWillSave método presentado anteriormente, puede tener UIKit o AppKit administrar automáticamente la actividad del usuario. Para ello, establezca la propiedad del UserActivity objeto respondedor e implemente el UpdateUserActivityState método . Para obtener más información, consulte la sección Entrega de soporte técnico en respondedor.

Compatibilidad con App Framework

Tanto UIKit (iOS) como AppKit (OS X) proporcionan compatibilidad integrada con Handoff en las NSDocumentclases , Responder (NSResponderUIResponder/) y .AppDelegate Aunque cada sistema operativo implementa Handoff ligeramente diferente, el mecanismo básico y las API son los mismos.

Actividades de usuario en aplicaciones basadas en documentos

Las aplicaciones iOS y OS X basadas en documentos tienen automáticamente compatibilidad integrada con handoff. Para activar esta compatibilidad, deberá agregar una clave y un NSUbiquitousDocumentUserActivityType valor para cada CFBundleDocumentTypes entrada en el archivo Info.plist de la aplicación.

Si esta clave está presente, tanto NSDocument como UIDocument crea NSUserActivity automáticamente instancias para documentos basados en iCloud del tipo especificado. Deberá proporcionar un tipo de actividad para cada tipo de documento que admita la aplicación y varios tipos de documento puedan usar el mismo tipo de actividad. Tanto como NSDocumentUIDocument rellenan automáticamente la UserInfo propiedad de NSUserActivity con el valor de su FileURL propiedad.

En OS X, los NSUserActivity administrados por AppKit y asociados a los respondedores se convierten automáticamente en la actividad actual cuando la ventana del documento se convierte en la ventana principal. En iOS, para NSUserActivity los objetos administrados por UIKit, debe llamar explícitamente al BecomeCurrent método o hacer que la propiedad del UserActivity documento esté establecida en un UIViewController cuando la aplicación llegue al primer plano.

AppKit restaurará automáticamente cualquier UserActivity propiedad creada de esta manera en OS X. Esto ocurre si el ContinueUserActivity método devuelve false o si no se implementa. En esta situación, el documento se abre con el OpenDocument método de NSDocumentController y, a continuación, recibirá una RestoreUserActivityState llamada al método .

Consulte la sección Compatibilidad con entrega en aplicaciones basadas en documentos a continuación para obtener más información.

Actividades y respondedores de usuario

Tanto UIKit como AppKit pueden administrar automáticamente una actividad de usuario si la establece como propiedad de UserActivity un objeto de respondedor. Si se ha modificado el estado, deberá establecer la NeedsSave propiedad del respondedor UserActivitytrueen . El sistema guardará automáticamente cuando UserActivity sea necesario, después de proporcionar el tiempo del respondedor para actualizar el estado llamando a su UpdateUserActivityState método.

Si varios respondedores comparten una sola NSUserActivity instancia, reciben una UpdateUserActivityState devolución de llamada cuando el sistema actualiza el objeto de actividad de usuario. El respondedor debe llamar al AddUserInfoEntries método para actualizar el NSUserActivitydiccionario del UserInfo objeto para reflejar el estado de actividad actual en este momento. El UserInfo diccionario se borra antes de cada UpdateUserActivityState llamada.

Para desasociarse de una actividad, un respondedor puede establecer su UserActivity propiedad en null. Cuando una instancia administrada NSUserActivity de app Framework no tiene más respondedor o documentos asociados, se invalida automáticamente.

Para obtener más información, consulte la sección Entrega de soporte técnico en respondedor.

Actividades de usuario y AppDelegate

El de la AppDelegate aplicación es su punto de entrada principal al controlar una continuación de entrega. Cuando el usuario responde a una notificación de entrega, se inicia la aplicación adecuada (si aún no se está ejecutando) y se llama al WillContinueUserActivityWithType método de .AppDelegate En este momento, la aplicación debe informar al usuario de que se está iniciando la continuación.

La NSUserActivity instancia se entrega cuando se llama al AppDelegatemétodo de ContinueUserActivity . En este momento, debes configurar la interfaz de usuario de la aplicación y continuar con la actividad especificada.

Consulte la sección Implementación de entrega a continuación para obtener más información.

Habilitación de la entrega en una aplicación de Xamarin

Debido a los requisitos de seguridad impuestos por Handoff, una aplicación de Xamarin.iOS que usa el marco Handoff debe configurarse correctamente en el Portal para desarrolladores de Apple y en el archivo de proyecto de Xamarin.iOS.

Haga lo siguiente:

  1. Inicie sesión en el Portal para desarrolladores de Apple.

  2. Haga clic en Certificados, Identificadores y Perfiles.

  3. Si aún no lo ha hecho, haga clic en Identificadores y cree un identificador para la aplicación (por ejemplo com.company.appname, ), edite el identificador existente.

  4. Asegúrese de que el servicio iCloud se ha comprobado para el identificador especificado:

    Enable the iCloud service for the given ID

  5. Guarde los cambios.

  6. Haga clic en Desarrollo de perfiles>de aprovisionamiento y cree un nuevo perfil de aprovisionamiento de desarrollo para su aplicación:

    Create a new development provisioning profile for the app

  7. Descargue e instale el nuevo perfil de aprovisionamiento o use Xcode para descargar e instalar el perfil.

  8. Edite las opciones del proyecto de Xamarin.iOS y asegúrese de que usa el perfil de aprovisionamiento que acaba de crear:

    Select the provisioning profile just created

  9. A continuación, edite el archivo Info.plist y asegúrese de que usa el identificador de aplicación que se usó para crear el perfil de aprovisionamiento:

    Set App ID

  10. Desplácese hasta la sección Modos de fondo y compruebe los siguientes elementos:

    Enable the required background modes

  11. Guarde los cambios en todos los archivos.

Con esta configuración en su lugar, la aplicación ya está lista para acceder a las API de Handoff Framework. Para obtener información detallada sobre el aprovisionamiento, consulte nuestras guías de aprovisionamiento y aprovisionamiento de dispositivos.

Implementación de la entrega

Las actividades de usuario se pueden continuar entre las aplicaciones que están firmadas con el mismo identificador de equipo de desarrollador y admiten el mismo tipo de actividad. La implementación de Handoff en una aplicación de Xamarin.iOS requiere que cree un objeto de actividad de usuario (ya sea en UIKit o AppKit), actualice el estado del objeto para realizar el seguimiento de la actividad y continuar con la actividad en un dispositivo receptor.

Identificación de actividades de usuario

El primer paso para implementar Handoff es identificar los tipos de actividades de usuario que admite la aplicación y ver cuáles de esas actividades son buenos candidatos para la continuación en otro dispositivo. Por ejemplo: una aplicación ToDo podría admitir la edición de elementos como un tipo de actividad de usuario y admitir la exploración de la lista de elementos disponibles como otra.

Una aplicación puede crear tantos tipos de actividad de usuario como sea necesario, uno para cualquier función que proporcione la aplicación. Para cada tipo de actividad de usuario, la aplicación tendrá que realizar un seguimiento de cuándo comienza y finaliza una actividad del tipo, y debe mantener la información de estado actualizada para continuar esa tarea en otro dispositivo.

Las actividades de usuario se pueden continuar en cualquier aplicación firmada con el mismo identificador de equipo sin ninguna asignación uno a uno entre las aplicaciones de envío y recepción. Por ejemplo, una aplicación determinada puede crear cuatro tipos diferentes de actividades, que se consumen por diferentes aplicaciones individuales en otro dispositivo. Se trata de una aparición común entre una versión mac de la aplicación (que puede tener muchas características y funciones) y aplicaciones de iOS, donde cada aplicación es más pequeña y se centra en una tarea específica.

Creación de identificadores de tipo de actividad

El identificador de tipo de actividad es una cadena corta agregada a la NSUserActivityTypes matriz del archivo Info.plist de la aplicación que se usa para identificar de forma única un tipo de actividad de usuario determinado. Habrá una entrada en la matriz para cada actividad que admita la aplicación. Apple sugiere usar una notación de estilo DNS inverso para el identificador de tipo de actividad para evitar colisiones. Por ejemplo: com.company-name.appname.activity para actividades específicas basadas en aplicaciones o com.company-name.activity para actividades que se pueden ejecutar en varias aplicaciones.

El identificador de tipo de actividad se usa al crear una NSUserActivity instancia para identificar el tipo de actividad. Cuando una actividad continúa en otro dispositivo, el tipo de actividad (junto con el identificador de equipo de la aplicación) determina qué aplicación se iniciará para continuar con la actividad.

Por ejemplo, vamos a crear una aplicación de ejemplo denominada MonkeyBrowser (descargar aquí). Esta aplicación presentará cuatro pestañas, cada una con una dirección URL diferente abierta en una vista del explorador web. El usuario podrá continuar con cualquier pestaña de un dispositivo iOS diferente que ejecute la aplicación.

Para crear los identificadores de tipo de actividad necesarios para admitir este comportamiento, edite el archivo Info.plist y cambie a la vista Origen . Agregue una NSUserActivityTypes clave y cree los siguientes identificadores:

The NSUserActivityTypes key and required identifiers in the plist editor

Hemos creado cuatro nuevos identificadores de tipo de actividad, uno para cada una de las pestañas de la aplicación MonkeyBrowser de ejemplo. Al crear sus propias aplicaciones, reemplace el contenido de la NSUserActivityTypes matriz por los identificadores de tipo de actividad específicos de las actividades que admite la aplicación.

Seguimiento de cambios en la actividad del usuario

Al crear una nueva instancia de la NSUserActivity clase , especificaremos una instancia para realizar un NSUserActivityDelegate seguimiento de los cambios en el estado de la actividad. Por ejemplo, el código siguiente se puede usar para realizar un seguimiento de los cambios de estado:

using System;
using CoreGraphics;
using Foundation;
using UIKit;

namespace MonkeyBrowse
{
    public class UserActivityDelegate : NSUserActivityDelegate
    {
        #region Constructors
        public UserActivityDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override void UserActivityReceivedData (NSUserActivity userActivity, NSInputStream inputStream, NSOutputStream outputStream)
        {
            // Log
            Console.WriteLine ("User Activity Received Data: {0}", userActivity.Title);
        }

        public override void UserActivityWasContinued (NSUserActivity userActivity)
        {
            Console.WriteLine ("User Activity Was Continued: {0}", userActivity.Title);
        }

        public override void UserActivityWillSave (NSUserActivity userActivity)
        {
            Console.WriteLine ("User Activity will be Saved: {0}", userActivity.Title);
        }
        #endregion
    }
}

Se llama al UserActivityReceivedData método cuando un flujo de continuación ha recibido datos de un dispositivo de envío. Para obtener más información, consulte la sección De continuación auxiliar Secuencias siguiente.

Se llama al UserActivityWasContinued método cuando otro dispositivo ha tomado control de una actividad del dispositivo actual. Según el tipo de actividad, como agregar un nuevo elemento a una lista ToDo, es posible que la aplicación necesite anular la actividad en el dispositivo de envío.

Se UserActivityWillSave llama al método antes de que los cambios realizados en la actividad se guarden y sincronicen entre dispositivos disponibles localmente. Puede usar este método para realizar cualquier cambio de última hora en la UserInfo propiedad de la NSUserActivity instancia antes de enviarlo.

Creación de una instancia de NSUserActivity

Cada actividad que la aplicación desee proporcionar la posibilidad de continuar en otro dispositivo debe estar encapsulada en una NSUserActivity instancia de . La aplicación puede crear tantas actividades como sea necesario y la naturaleza de esas actividades depende de la funcionalidad y las características de la aplicación en cuestión. Por ejemplo, una aplicación de correo electrónico podría crear una actividad para crear un nuevo mensaje y otra para leer un mensaje.

Para nuestra aplicación de ejemplo, se crea una nueva NSUserActivity cada vez que el usuario escribe una nueva dirección URL en una de las vistas del explorador web con pestañas. El código siguiente almacena el estado de una pestaña determinada:

public NSString UserActivityTab1 = new NSString ("com.xamarin.monkeybrowser.tab1");
public NSUserActivity UserActivity { get; set; }
...

UserActivity = new NSUserActivity (UserActivityTab1);
UserActivity.Title = "Weather Tab";
UserActivity.Delegate = new UserActivityDelegate ();

// Update the activity when the tab's URL changes
var userInfo = new NSMutableDictionary ();
userInfo.Add (new NSString ("Url"), new NSString (url));
UserActivity.AddUserInfoEntries (userInfo);

// Inform Activity that it has been updated
UserActivity.BecomeCurrent ();

Crea un nuevo NSUserActivity con uno de los tipos de actividad de usuario creados anteriormente y proporciona un título legible para la actividad. Se asocia a una instancia de la NSUserActivityDelegate creada anteriormente para ver los cambios de estado e informa a iOS de que esta actividad de usuario es la actividad actual.

Rellenar el diccionario UserInfo

Como hemos visto anteriormente, la UserInfo propiedad de la NSUserActivity clase es un NSDictionary de pares clave-valor usados para definir el estado de una actividad determinada. Los valores almacenados en UserInfo deben ser uno de los siguientes tipos: NSArray, NSData, NSDictionaryNSDateNSNumberNSNull, , NSSet, NSStringo .NSURL NSURL los valores de datos que apuntan a documentos de iCloud se ajustarán automáticamente para que apunten a los mismos documentos en un dispositivo receptor.

En el ejemplo anterior, creamos un NSMutableDictionary objeto y lo rellenamos con una sola clave que proporcionaba la dirección URL que el usuario estaba viendo actualmente en la pestaña especificada. El AddUserInfoEntries método de la actividad de usuario se usó para actualizar la actividad con los datos que se usarán para restaurar la actividad en el dispositivo receptor:

// Update the activity when the tab's URL changes
var userInfo = new NSMutableDictionary ();
userInfo.Add (new NSString ("Url"), new NSString (url));
UserActivity.AddUserInfoEntries (userInfo);

Apple sugiere mantener la información enviada al mínimo más bar para asegurarse de que la actividad se envía de forma oportuna al dispositivo receptor. Si se requiere información más grande, como es necesario enviar una imagen adjunta a un documento, debe usar La continuación Secuencias. Consulte la sección De continuación auxiliar Secuencias a continuación para obtener más información.

Continuar con una actividad

Handoff informará automáticamente a los dispositivos iOS y OS X locales que están en proximidad física al dispositivo de origen e iniciaron sesión en la misma cuenta de iCloud, de la disponibilidad de actividades de usuario continuas. Si el usuario decide continuar una actividad en un nuevo dispositivo, el sistema iniciará la aplicación adecuada (en función del identificador de equipo y el tipo de actividad) e información sobre su AppDelegate continuación debe producirse.

En primer lugar, se llama al WillContinueUserActivityWithType método para que la aplicación pueda informar al usuario de que la continuación está a punto de comenzar. Usamos el código siguiente en el archivo AppDelegate.cs de nuestra aplicación de ejemplo para controlar un inicio de continuación:

public NSString UserActivityTab1 = new NSString ("com.xamarin.monkeybrowser.tab1");
public NSString UserActivityTab2 = new NSString ("com.xamarin.monkeybrowser.tab2");
public NSString UserActivityTab3 = new NSString ("com.xamarin.monkeybrowser.tab3");
public NSString UserActivityTab4 = new NSString ("com.xamarin.monkeybrowser.tab4");
...

public FirstViewController Tab1 { get; set; }
public SecondViewController Tab2 { get; set;}
public ThirdViewController Tab3 { get; set; }
public FourthViewController Tab4 { get; set; }
...

public override bool WillContinueUserActivity (UIApplication application, string userActivityType)
{
    // Report Activity
    Console.WriteLine ("Will Continue Activity: {0}", userActivityType);

    // Take action based on the user activity type
    switch (userActivityType) {
    case "com.xamarin.monkeybrowser.tab1":
        // Inform view that it's going to be modified
        Tab1.PreparingToHandoff ();
        break;
    case "com.xamarin.monkeybrowser.tab2":
        // Inform view that it's going to be modified
        Tab2.PreparingToHandoff ();
        break;
    case "com.xamarin.monkeybrowser.tab3":
        // Inform view that it's going to be modified
        Tab3.PreparingToHandoff ();
        break;
    case "com.xamarin.monkeybrowser.tab4":
        // Inform view that it's going to be modified
        Tab4.PreparingToHandoff ();
        break;
    }

    // Inform system we handled this
    return true;
}

En el ejemplo anterior, cada controlador de vista se registra con AppDelegate y tiene un método público PreparingToHandoff que muestra un indicador de actividad y un mensaje que indica al usuario que la actividad está a punto de entregarse al dispositivo actual. Ejemplo:

private void ShowBusy(string reason) {

    // Display reason
    BusyText.Text = reason;

    //Define Animation
    UIView.BeginAnimations("Show");
    UIView.SetAnimationDuration(1.0f);

    Handoff.Alpha = 0.5f;

    //Execute Animation
    UIView.CommitAnimations();
}
...

public void PreparingToHandoff() {
    // Inform caller
    ShowBusy ("Continuing Activity...");
}

se ContinueUserActivity llamará a de AppDelegate para continuar realmente con la actividad especificada. De nuevo, desde nuestra aplicación de ejemplo:

public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{

    // Report Activity
    Console.WriteLine ("Continuing User Activity: {0}", userActivity.ToString());

    // Get input and output streams from the Activity
    userActivity.GetContinuationStreams ((NSInputStream arg1, NSOutputStream arg2, NSError arg3) => {
        // Send required data via the streams
        // ...
    });

    // Take action based on the Activity type
    switch (userActivity.ActivityType) {
    case "com.xamarin.monkeybrowser.tab1":
        // Preform handoff
        Tab1.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab1});
        break;
    case "com.xamarin.monkeybrowser.tab2":
        // Preform handoff
        Tab2.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab2});
        break;
    case "com.xamarin.monkeybrowser.tab3":
        // Preform handoff
        Tab3.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab3});
        break;
    case "com.xamarin.monkeybrowser.tab4":
        // Preform handoff
        Tab4.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab4});
        break;
    }

    // Inform system we handled this
    return true;
}

El método público PerformHandoff de cada controlador de vista preforma realmente la entrega y restaura la actividad en el dispositivo actual. En el caso del ejemplo, muestra la misma dirección URL en una pestaña determinada que el usuario estaba navegando en un dispositivo diferente. Ejemplo:

private void HideBusy() {

    //Define Animation
    UIView.BeginAnimations("Hide");
    UIView.SetAnimationDuration(1.0f);

    Handoff.Alpha = 0f;

    //Execute Animation
    UIView.CommitAnimations();
}
...

public void PerformHandoff(NSUserActivity activity) {

    // Hide busy indicator
    HideBusy ();

    // Extract URL from dictionary
    var url = activity.UserInfo ["Url"].ToString ();

    // Display value
    URL.Text = url;

    // Display the give webpage
    WebView.LoadRequest(new NSUrlRequest(NSUrl.FromString(url)));

    // Save activity
    UserActivity = activity;
    UserActivity.BecomeCurrent ();

}

El ContinueUserActivity método incluye un UIApplicationRestorationHandler que se puede llamar para reanudar la actividad basada en documentos o respondedor. Deberá pasar objetos NSArray restaurables o al controlador de restauración cuando se llame a . Por ejemplo:

completionHandler (new NSObject[]{Tab4});

Para cada objeto pasado, se llamará a su RestoreUserActivityState método . Después, cada objeto puede usar los datos del UserInfo diccionario para restaurar su propio estado. Por ejemplo:

public override void RestoreUserActivityState (NSUserActivity activity)
{
    base.RestoreUserActivityState (activity);

    // Log activity
    Console.WriteLine ("Restoring Activity {0}", activity.Title);
}

En el caso de las aplicaciones basadas en documentos, si no implementa el ContinueUserActivity método o devuelve false, UIKit o AppKit puede reanudar automáticamente la actividad. Consulte la sección Compatibilidad con entrega en aplicaciones basadas en documentos a continuación para obtener más información.

Error de entrega correctamente

Dado que Handoff se basa en la transmisión de información entre una colección conectada de forma flexible de dispositivos iOS y OS X, el proceso de transferencia a veces puede producir un error. Debes diseñar la aplicación para controlar estos errores correctamente e informar al usuario de las situaciones que surjan.

En caso de error, se llamará al DidFailToContinueUserActivitiy método de .AppDelegate Por ejemplo:

public override void DidFailToContinueUserActivitiy (UIApplication application, string userActivityType, NSError error)
{
    // Log information about the failure
    Console.WriteLine ("User Activity {0} failed to continue. Error: {1}", userActivityType, error.LocalizedDescription);
}

Debe usar el proporcionado NSError para proporcionar información al usuario sobre el error.

Entrega de aplicación nativa al explorador web

Es posible que un usuario quiera continuar una actividad sin tener instalada una aplicación nativa adecuada en el dispositivo deseado. En algunas situaciones, una interfaz basada en web puede proporcionar la funcionalidad necesaria y la actividad todavía se puede continuar. Por ejemplo, la cuenta de correo electrónico del usuario puede proporcionar una interfaz de usuario base web para redactar y leer mensajes.

Si se origina, la aplicación nativa conoce la dirección URL de la interfaz web (y la sintaxis necesaria para identificar el elemento dado que continúa), puede codificar esta información en la WebpageURL propiedad de la NSUserActivity instancia. Si el dispositivo receptor no tiene instalada una aplicación nativa adecuada para controlar la continuación, se puede llamar a la interfaz web proporcionada.

Entrega del explorador web a la aplicación nativa

Si el usuario usaba una interfaz basada en web en el dispositivo de origen y una aplicación nativa en el dispositivo receptor reclama la parte de dominio de la WebpageURL propiedad, el sistema usará esa aplicación para controlar la continuación. El nuevo dispositivo recibirá una NSUserActivity instancia que marca el tipo de actividad como BrowsingWeb y WebpageURL contendrá la dirección URL que el usuario estaba visitando, el UserInfo diccionario estará vacío.

Para que una aplicación participe en este tipo de Handoff, debe reclamar el dominio en un com.apple.developer.associated-domains derecho con el formato <service>:<fully qualified domain name> (por ejemplo: activity continuation:company.com).

Si el dominio especificado coincide con el valor de una WebpageURL propiedad, Handoff descarga una lista de identificadores de aplicación aprobados del sitio web en ese dominio. El sitio web debe proporcionar una lista de identificadores aprobados en un archivo JSON firmado denominado apple-app-site-association (por ejemplo, https://company.com/apple-app-site-association).

Este archivo JSON contiene un diccionario que especifica una lista de identificadores de aplicación con el formato <team identifier>.<bundle identifier>. Por ejemplo:

{
    "activitycontinuation": {
        "apps": [    "YWBN8XTPBJ.com.company.FirstApp",
            "YWBN8XTPBJ.com.company.SecondApp" ]
    }
}

Para firmar el archivo JSON (de modo que tenga el valor correcto Content-Type de application/pkcs7-mime), use la aplicación Terminal y un comando con un openssl certificado y una clave emitidos por una entidad de certificación de confianza para iOS (consulte https://support.apple.com/kb/ht5012 para obtener una lista). Por ejemplo:

echo '{"activitycontinuation":{"apps":["YWBN8XTPBJ.com.company.FirstApp",
"YWBN8XTPBJ.com.company.SecondApp"]}}' > json.txt

cat json.txt | openssl smime -sign -inkey company.com.key
-signer company.com.pem
-certfile intermediate.pem
-noattr -nodetach
-outform DER > apple-app-site-association

El openssl comando genera un archivo JSON firmado que coloca en el sitio web en la dirección URL de apple-app-site-association . Por ejemplo:

https://example.com/apple-app-site-association.

La aplicación recibirá cualquier actividad cuyo WebpageURL dominio esté en su com.apple.developer.associated-domains derecho. Solo se admiten los http protocolos y https , cualquier otro protocolo generará una excepción.

Compatibilidad con la entrega en aplicaciones basadas en documentos

Como se indicó anteriormente, en iOS y OS X, las aplicaciones basadas en documentos admitirán automáticamente la entrega de documentos basados en iCloud si el archivo Info.plist de la aplicación contiene una CFBundleDocumentTypes clave de NSUbiquitousDocumentUserActivityType. Por ejemplo:

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>NSRTFDPboardType</string>
        . . .
        <key>LSItemContentTypes</key>
        <array>
        <string>com.myCompany.rtfd</string>
        </array>
        . . .
        <key>NSUbiquitousDocumentUserActivityType</key>
        <string>com.myCompany.myEditor.editing</string>
    </dict>
</array>

En este ejemplo, la cadena es un designador de aplicación DNS inverso con el nombre de la actividad anexada. Si se especifica de esta manera, no es necesario repetir las entradas del tipo de actividad en la NSUserActivityTypes matriz del archivo Info.plist .

Otros objetos de la aplicación pueden hacer referencia al objeto Actividad de usuario creado automáticamente (disponible a través de la propiedad del UserActivity documento) y se usan para restaurar el estado en la continuación. Por ejemplo, para realizar un seguimiento de la selección de elementos y la posición del documento. Debe establecer esta propiedad true activities NeedsSave en cada vez que el estado cambie y actualice el UserInfo diccionario en el UpdateUserActivityState método .

La UserActivity propiedad se puede usar desde cualquier subproceso y se ajusta al protocolo de observación de clave-valor (KVO), por lo que se puede usar para mantener un documento sincronizado a medida que se mueve hacia y fuera de iCloud. La UserActivity propiedad se invalidará cuando se cierre el documento.

Para obtener más información, consulte La compatibilidad con la actividad de usuario de Apple en la documentación de aplicaciones basadas en documentos.

Apoyo de entrega en respondedor

Puede asociar respondedores (heredados de UIResponder en iOS o NSResponder en OS X) a actividades estableciendo sus UserActivity propiedades. El sistema guarda automáticamente la UserActivity propiedad en los momentos adecuados, llamando al método del UpdateUserActivityState respondedor para agregar datos actuales al objeto Actividad del usuario mediante el AddUserInfoEntriesFromDictionary método .

Compatibilidad con Secuencias de continuación

Puede ser situaciones en las que la cantidad de información necesaria para continuar con una actividad no se puede transferir eficazmente mediante la carga de entrega inicial. En estas situaciones, la aplicación receptora puede establecer una o varias secuencias entre sí y la aplicación de origen para transferir los datos.

La aplicación de origen establecerá la SupportsContinuationStreams propiedad de la NSUserActivity instancia trueen . Por ejemplo:

// Create a new user Activity to support this tab
UserActivity = new NSUserActivity (ThisApp.UserActivityTab1){
    Title = "Weather Tab",
    SupportsContinuationStreams = true
};
UserActivity.Delegate = new UserActivityDelegate ();

// Update the activity when the tab's URL changes
var userInfo = new NSMutableDictionary ();
userInfo.Add (new NSString ("Url"), new NSString (url));
UserActivity.AddUserInfoEntries (userInfo);

// Inform Activity that it has been updated
UserActivity.BecomeCurrent ();

A continuación, la aplicación receptora puede llamar al GetContinuationStreams método de NSUserActivity en para AppDelegate establecer la secuencia. Por ejemplo:

public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{

    // Report Activity
    Console.WriteLine ("Continuing User Activity: {0}", userActivity.ToString());

    // Get input and output streams from the Activity
    userActivity.GetContinuationStreams ((NSInputStream arg1, NSOutputStream arg2, NSError arg3) => {
        // Send required data via the streams
        // ...
    });

    // Take action based on the Activity type
    switch (userActivity.ActivityType) {
    case "com.xamarin.monkeybrowser.tab1":
        // Preform handoff
        Tab1.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab1});
        break;
    case "com.xamarin.monkeybrowser.tab2":
        // Preform handoff
        Tab2.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab2});
        break;
    case "com.xamarin.monkeybrowser.tab3":
        // Preform handoff
        Tab3.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab3});
        break;
    case "com.xamarin.monkeybrowser.tab4":
        // Preform handoff
        Tab4.PerformHandoff (userActivity);
        completionHandler (new NSObject[]{Tab4});
        break;
    }

    // Inform system we handled this
    return true;
}

En el dispositivo de origen, el delegado de actividad de usuario recibe las secuencias llamando a su DidReceiveInputStream método para proporcionar los datos solicitados para continuar la actividad del usuario en el dispositivo de reanudación.

Usará para NSInputStream proporcionar acceso de solo lectura a los datos de flujo y proporcionar NSOutputStream acceso de solo escritura. Las secuencias deben usarse de forma de solicitud y respuesta, donde la aplicación receptora solicita más datos y la aplicación de origen la proporciona. Por lo tanto, los datos escritos en el flujo de salida en el dispositivo de origen se leen desde el flujo de entrada en el dispositivo continuo y viceversa.

Incluso en situaciones en las que se requiera el flujo de continuación, debe haber una comunicación mínima entre las dos aplicaciones.

Para obtener más información, consulte la documentación sobre el uso de continuación de Apple Secuencias.

Procedimientos recomendados de entrega

La implementación correcta de la continuación sin problemas de una actividad de usuario a través de Handoff requiere un diseño cuidadoso debido a todos los distintos componentes implicados. Apple sugiere adoptar los procedimientos recomendados siguientes para las aplicaciones habilitadas para handoff:

  • Diseñe las actividades de usuario para requerir la carga más pequeña posible para relacionar el estado de la actividad que se va a continuar. Cuanto mayor sea la carga, más tiempo tardará la continuación en iniciarse.
  • Si debe transferir grandes cantidades de datos para una continuación correcta, tenga en cuenta los costos implicados en la configuración y la sobrecarga de red.
  • Es habitual que una aplicación mac grande cree actividades de usuario que se controlan mediante varias aplicaciones más pequeñas y específicas de tareas en dispositivos iOS. Las distintas versiones de la aplicación y del sistema operativo deben diseñarse para funcionar bien juntas o con errores correctamente.
  • Al especificar los tipos de actividad, use la notación de DNS inverso para evitar colisiones. Si una actividad es específica de una aplicación determinada, su nombre debe incluirse en la definición de tipo (por ejemplo com.myCompany.myEditor.editing). Si la actividad puede funcionar en varias aplicaciones, quite el nombre de la aplicación de la definición (por ejemplo com.myCompany.editing, ).
  • Si la aplicación necesita actualizar el estado de una actividad de usuario (NSUserActivity) establezca la NeedsSave propiedad trueen . En los momentos adecuados, Handoff llamará al método del UserActivityWillSave delegado para que pueda actualizar el UserInfo diccionario según sea necesario.
  • Dado que es posible que el proceso de entrega no se inicialice al instante en el dispositivo receptor, debe implementar los AppDelegate"s WillContinueUserActivity " e informar al usuario de que una continuación está a punto de iniciarse.

Aplicación de entrega de ejemplo

Como ejemplo de uso de Handoff en una aplicación de Xamarin.iOS, hemos incluido la aplicación de ejemplo MonkeyBrowser con esta guía. La aplicación tiene cuatro pestañas que el usuario puede usar para examinar la web, cada una con un tipo de actividad determinado: Weather, Favorite, Coffee Break y Work.

En cualquier pestaña, cuando el usuario escribe una nueva dirección URL y pulsa el botón Ir , se crea una nueva NSUserActivity pestaña para esa pestaña que contiene la dirección URL que el usuario está explorando actualmente:

Example Handoff App

Si otro de los dispositivos del usuario tiene instalada la aplicación MonkeyBrowser , inicia sesión en iCloud con la misma cuenta de usuario, está en la misma red y está cerca del dispositivo anterior, la actividad de entrega se mostrará en la pantalla principal (en la esquina inferior izquierda):

The Handoff Activity displayed on the home screen in the lower left hand corner

Si el usuario se arrastra hacia arriba en el icono De entrega, se iniciará la aplicación y la actividad de usuario especificada en el NSUserActivity se continuará en el nuevo dispositivo:

The User Activity continued on the new device

Cuando la actividad de usuario se ha enviado correctamente a otro dispositivo Apple, el dispositivo NSUserActivity de envío recibirá una llamada al UserActivityWasContinued método en su NSUserActivityDelegate para informarle de que la actividad del usuario se ha transferido correctamente a otro dispositivo.

Resumen

En este artículo se ha proporcionado una introducción al marco handoff usado para continuar una actividad de usuario entre varios de los dispositivos Apple del usuario. A continuación, se mostró cómo habilitar e implementar Handoff en una aplicación de Xamarin.iOS. Por último, se trataron los diferentes tipos de continuaciones de entrega disponibles y los procedimientos recomendados de entrega.