Handoff 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.

Ejemplo de realización de una operación de Handoff

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 iPhone 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 aplicaciones basadas en NSDocument o UIDocument tienen automáticamente compatibilidad integrada con Handoff y requieren cambios mínimos para admitir Handoff.

Actividades continuas del usuario

La clase NSUserActivity (junto con algunos pequeños cambios en UIKit 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, se debe encapsular en una instancia NSUserActivity, marcada como Actividad actual, tener establecido la carga (los datos usados para realizar la continuación) y la actividad debe transmitirse a ese dispositivo.

Handoff pasa el mínimo de información para definir la actividad que se va a continuar, y los paquetes de datos más grandes 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.

Información general sobre las actividades continuas del usuario

Solo las aplicaciones que comparten el mismo identificador de equipo de desarrollador y responden a un determinado Tipo de actividad son aptas para la continuación. Una aplicación define los tipos de actividad que admite en la clave NSUserActivityTypes 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 actividad.

La aplicación receptora usa información del diccionario NSUserActivity de 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 un NSUserActivity, la 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 Secuencias de continuación auxiliares a continuación.

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ónCompatibilidad con entrega en aplicaciones basadas en documentos a continuación.

La clase NSUserActivity

La clase NSUserActivity es el objeto principal de un intercambio Handoff 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 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 propiedades ActivityType y Title. La propiedad de diccionario UserInfo se usa para llevar información sobre el estado de la actividad. Establezca la propiedad NeedsSave en true si desea cargar la información de estado diferida a través del delegado de NSUserActivity. Use el método AddUserInfoEntries para combinar nuevos datos de otros clientes en el diccionario UserInfo según sea necesario para conservar el estado de la actividad.

La clase NSUserActivityDelegate

NSUserActivityDelegate se usa para mantener la información en un diccionario UserInfo deNSUserActivityactualizado 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.

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

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

Compatibilidad con App Framework

Tanto UIKit (iOS) como AppKit (OS X) proporcionan compatibilidad integrada con Handoff en las clases NSDocument, Responded (UIResponder/NSResponder) 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 NSUbiquitousDocumentUserActivityType y un valor para cada entrada de CFBundleDocumentTypes en el archivo info.plist de la aplicación.

Si esta clave está presente, tanto NSDocument como UIDocument crea NSUserActivityautomá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 NSDocument como UIDocument rellenan automáticamente la propiedad UserInfo del NSUserActivity con el valor de su propiedad FileURL.

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

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

Consulte la sección a continuación:Compatibilidad con entrega en aplicaciones basadas en documentos 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 UserActivity de un objeto respondedor. Si se ha modificado el estado, deberá establecer la propiedad NeedsSave del respondedor de UserActivity a true. El sistema guardará automáticamente a UserActivity cuando sea necesario, después de proporcionar el tiempo del respondedor para actualizar el estado llamando a su método UpdateUserActivityState.

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

Para desasociarse de una actividad, un respondedor puede establecer su propiedad UserActivity en null. Cuando una instancia NSUserActivity administrada por 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 a continuación.

Actividades de usuario y AppDelegate

El de la aplicación AppDelegate 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 método WillContinueUserActivityWithType del AppDelegate. En este momento, la aplicación debe informar al usuario de que se está iniciando la continuación.

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

Consulte la sección Implementación de Handoff 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 servicioiCloud se ha comprobado para el identificador especificado:

    Habilitar el servicio iCloud para el id. especificado

  5. Guarde los cambios.

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

    Crear un nuevo perfil de aprovisionamiento de desarrollo para la aplicación

  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:

    Seleccione el perfil de aprovisionamiento que acaba de crear

  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:

    Establecer el identificador de aplicación

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

    Habilitar los modos de fondo necesarios

  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 Handoff

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 otro.

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 matriz NSUserActivityTypes 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 a fin de 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 instancia NSUserActivity 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. 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.

A fin de 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 clave NSUserActivityTypes y cree los siguientes identificadores:

Clave NSUserActivityTypes e identificadores necesarios en el editor de plist

Hemos creado cuatro nuevos identificadores de tipo de actividad, uno para cada una de las pestañas del ejemplo aplicaciónMonkeyBrowser. Al crear aplicaciones propias, reemplace el contenido de la matriz NSUserActivityTypes 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 clase NSUserActivity, especificaremos una instancia NSUserActivityDelegate para realizar un 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 método UserActivityReceivedData 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 Secuencias de continuación auxiliares a continuación.

Se llama al método UserActivityWasContinued cuando otro dispositivo ha tomado el 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 llama al método UserActivityWillSave 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 propiedad UserInfo de la instancia de NSUserActivity 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 instancia NSUserActivity. 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.

En 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 ();

Crear un nuevo NSUserActivity mediante 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 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 propiedad UserInfo de la clase NSUserActivity 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, NSDate, NSDictionary, NSNull, NSNumber, NSSet, NSString, o NSURL. NSURL 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 objeto NSMutableDictionary 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 método AddUserInfoEntries 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 secuencias de continuación. Consulte la sección Compatibilidad con secuencias de continuación 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 método WillContinueUserActivityWithType 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 de PreparingToHandoff público 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 llamará a la ContinueUserActivity del 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 realiza realmente el traspaso 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 método ContinueUserActivity incluye un UIApplicationRestorationHandler que puede llamar para reanudar la actividad basada en documentos o respondedor. Deberá pasar un NSArray u objetos restaurables al controlador de restauración cuando se llame. Por ejemplo:

completionHandler (new NSObject[]{Tab4});

Para cada objeto pasado, se llamará a su método RestoreUserActivityState. A continuación, cada objeto puede usar los datos del diccionario UserInfo 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 métodoContinueUserActivity o devuelve false, UIKit o AppKit puede reanudar automáticamente la actividad. Consulte la sección a continuación:Compatibilidad con entrega en aplicaciones basadas en documentos para obtener más información.

Error de Handoff de forma correcta

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. Debe 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 método DidFailToContinueUserActivitiy del 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.

Handoff de aplicación nativa a 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 propiedad WebpageURL de la instancia NSUserActivity. 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.

Handoff de 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 propiedad WebpageURL, el sistema usará esa aplicación para controlar la continuación. El nuevo dispositivo recibirá una instancia NSUserActivity que marca el tipo de actividad como BrowsingWeb y el WebpageURL contendrá la dirección URL que el usuario estaba visitando, el diccionario UserInfo estará vacío.

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

Si el dominio especificado coincide con el valor de una propiedad WebpageURL, 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 Content-Type correcto de application/pkcs7-mime), use la aplicación Terminal y un comando openssl con un 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 comando openssl 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 dominio de WebpageURL esté en su derecho com.apple.developer.associated-domains. Solo se admiten los protocolos http y https, cualquier otro protocolo generará una excepción.

Compatibilidad con la entrega en aplicaciones basadas en documentos

Como se ha indicado 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 de la aplicaciónInfo.plist contiene una clave CFBundleDocumentTypes 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 de tipo de actividad en la matriz NSUserActivityTypes 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 UserActivity del 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. Es necesario establecer esta propiedad de actividades NeedsSave a true siempre que cambie el estado y actualizar el diccionario UserInfo en el método UpdateUserActivityState.

La propiedad UserActivity 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 propiedadUserActivity se invalidará cuando se cierre el documento.

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

Apoyo de entrega en respondedor

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

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 en true. 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 métodoGetContinuationStreams de NSUserActivity en suAppDelegate para 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 método DidReceiveInputStream para proporcionar los datos solicitados para continuar la actividad del usuario en el dispositivo de reanudación.

Usará a NSInputStream para proporcionar acceso de solo lectura a los datos de flujo y a NSOutputStream para proporcionar 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 secuencias de continuación de Apple.

Procedimientos recomendados de entrega

La implementación correcta de la continuación sin interrupciones de una actividad de usuario a través de Handoff requiere un diseño cuidadoso debido a todos los distintos componentes involucrados. 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 propiedad NeedsSave en true. En los momentos adecuados, Handoff llamará al método UserActivityWillSave del delegado para que pueda actualizar el diccionarioUserInfo 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 WillContinueUserActivity de AppDelegate e informar al usuario de que una continuación está a punto de iniciarse.

Aplicación de entrega de ejemplo

Un ejemplo de uso de Handoff en una aplicación de Xamarin.iOS es la aplicación de ejemplo MonkeyBrowser. 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 para esa pestaña que contiene la dirección URL que el usuario está explorando actualmente:

Aplicación de entrega de ejemplo

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):

Actividad de Handoff que se muestra en la pantalla principal en la esquina inferior izquierda

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:

La actividad del usuario continuó en el nuevo dispositivo

Cuando la actividad de usuario se ha enviado correctamente a otro dispositivo Apple, el NSUserActivity del dispositivo de envío recibirá una llamada al método UserActivityWasContinued 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.