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.
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.
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
deNSUserActivity
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.
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 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 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 propiedadUserActivity
creada 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:
Inicie sesión en el Portal para desarrolladores de Apple.
Haga clic en certificados, identificadores y perfiles.
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.Asegúrese de que el servicioiCloud se ha comprobado para el identificador especificado:
Guarde los cambios.
Haga clic en Perfiles de aprovisionamiento>Desarrollo y cree un nuevo perfil de aprovisionamiento de desarrollo para la aplicación:
Descargue e instale el nuevo perfil de aprovisionamiento o use Xcode para descargar e instalar el perfil.
Edite las opciones del proyecto de Xamarin.iOS y asegúrese de que usa el perfil de aprovisionamiento que acaba de crear:
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:
Desplácese hasta la sección Modos de fondo y compruebe los siguientes elementos:
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:
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 ejemplocom.myCompany.editing
). - Si la aplicación necesita actualizar el estado de una actividad de usuario (
NSUserActivity
) establezca la propiedadNeedsSave
entrue
. En los momentos adecuados, Handoff llamará al métodoUserActivityWillSave
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
deAppDelegate
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:
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):
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:
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.