Transferência no Xamarin.iOS

Este artigo aborda o trabalho com o Handoff em um aplicativo Xamarin.iOS para transferir atividades do usuário entre aplicativos em execução em outros dispositivos do usuário.

A Apple introduziu o Handoff no iOS 8 e no OS X Yosemite (10.10) para fornecer um mecanismo comum para o usuário transferir atividades iniciadas em um de seus dispositivos, para outro dispositivo executando o mesmo aplicativo ou outro aplicativo que suporte a mesma atividade.

Um exemplo de execução de uma operação Handoff

Este artigo dará uma olhada rápida na habilitação do compartilhamento de atividades em um aplicativo Xamarin.iOS e abordará a estrutura Handoff em detalhes:

Sobre o Handoff

O Handoff (também conhecido como Continuity) foi introduzido pela Apple no iOS 8 e OS X Yosemite (10.10) como uma forma de o usuário iniciar uma atividade em um de seus dispositivos (iOS ou Mac) e continuar essa mesma atividade em outro de seus dispositivos (conforme identificado pela Conta do iCloud do usuário).

O Handoff foi expandido no iOS 9 para também oferecer suporte a novos recursos de pesquisa aprimorados. Para obter mais informações, consulte nossa documentação de aprimoramentos de pesquisa.

Por exemplo, o usuário pode iniciar um e-mail em seu iPhone e continuar perfeitamente o e-mail em seu Mac, com todas as mesmas informações de mensagem preenchidas e o cursor no mesmo local que ele deixou no iOS.

Qualquer um dos seus aplicativos que compartilham o mesmo ID de Equipe está qualificado para usar o Handoff para continuar as atividades do usuário em todos os aplicativos, desde que esses aplicativos sejam entregues por meio da iTunes App Store ou assinados por um desenvolvedor registrado (para aplicativos Mac, Enterprise ou Ad Hoc).

Todos os NSDocument aplicativos baseados UIDocument automaticamente têm suporte a Handoff integrado e exigem alterações mínimas para oferecer suporte a Handoff.

Atividades contínuas do usuário

A NSUserActivity classe (juntamente com algumas pequenas alterações em UIKit e AppKit) fornece suporte para definir a atividade de um usuário que pode ser potencialmente continuada em outro dispositivo do usuário.

Para que uma atividade seja passada para outro dispositivo do usuário, ela deve ser encapsulada em uma instância NSUserActivity, marcada como Atividade Atual, ter sua carga útil definida (os dados usados para executar a continuação) e a atividade deve ser transmitida para esse dispositivo.

O Handoff passa o mínimo de informações para definir a atividade a ser continuada, com pacotes de dados maiores sendo sincronizados via iCloud.

No dispositivo receptor, o usuário receberá uma notificação de que uma atividade está disponível para continuação. Se o usuário optar por continuar a atividade no novo dispositivo, o aplicativo especificado será iniciado (se ainda não estiver em execução) e a carga útil do NSUserActivity será usada para reiniciar a atividade.

Uma visão geral das atividades contínuas do usuário

Somente os aplicativos que compartilham a mesma ID da equipe do desenvolvedor e respondem a um determinado Tipo de Atividade são qualificados para continuação. Um aplicativo define os tipos de atividade que ele oferece suporte sob a NSUserActivityTypes chave de seu arquivo Info.plist . Diante disso, um dispositivo contínuo escolhe o aplicativo para executar a continuação com base na ID da Equipe, no Tipo de Atividade e, opcionalmente, no Título da Atividade.

O aplicativo de recebimento usa informações do NSUserActivitydicionário do UserInfo para configurar sua interface do usuário e restaurar o estado da atividade especificada para que a transição pareça perfeita para o usuário final.

Se a continuação exigir mais informações do que podem ser enviadas de forma eficiente por meio de um NSUserActivity, o aplicativo de retomada pode enviar uma chamada para o aplicativo de origem e estabelecer um ou mais fluxos para transmitir os dados necessários. Por exemplo, se a atividade estivesse editando um documento de texto grande com várias imagens, o streaming seria necessário para transferir as informações necessárias para continuar a atividade no dispositivo receptor. Para obter mais informações, consulte a seção Suportando fluxos de continuação abaixo.

Como dito acima, NSDocument os UIDocument aplicativos baseados automaticamente têm suporte a Handoff integrado. Para obter mais informações, consulte a seção Transferência de suporte em aplicativos baseados em documentos abaixo.

A classe NSUserActivity

A NSUserActivity classe é o objeto principal em uma troca Handoff e é usada para encapsular o estado de uma atividade do usuário que está disponível para continuação. Um aplicativo instanciará uma cópia de NSUserActivity qualquer atividade que ele ofereça suporte e deseje continuar em outro dispositivo. Por exemplo, o editor de documentos criaria uma atividade para cada documento atualmente aberto. No entanto, apenas o documento mais frontal (exibido na Janela ou Guia mais à frente) é a Atividade Atual e, portanto, está disponível para continuação.

Uma instância de é identificada NSUserActivity por suas ActivityType propriedades e Title . A UserInfo propriedade dictionary é usada para transportar informações sobre o estado da atividade. Defina a NeedsSave propriedade como true se você quiser carregar preguiçosamente as informações de estado por meio do NSUserActivityrepresentante do . Use o AddUserInfoEntries método para mesclar novos dados de outros clientes no dicionário, UserInfo conforme necessário para preservar o estado da atividade.

A classe NSUserActivityDelegate

O NSUserActivityDelegate é usado para manter as informações em um NSUserActivitydicionário do atualizadas UserInfo e em sincronia com o estado atual da atividade. Quando o sistema precisa que as informações da atividade sejam atualizadas (como antes da continuação em outro dispositivo), ele chama o UserActivityWillSave método do delegado.

Você precisará implementar o UserActivityWillSave método e fazer quaisquer alterações no NSUserActivity (como UserInfo, Title, etc.) para garantir que ele ainda reflita o estado da Atividade Atual. Quando o sistema chamar o UserActivityWillSave método, o NeedsSave sinalizador será limpo. Se você modificar qualquer uma das propriedades de dados da atividade, precisará definir NeedsSave como true novamente.

Em vez de usar o UserActivityWillSave método apresentado acima, você pode opcionalmente ter UIKit ou AppKit gerenciar a atividade do usuário automaticamente. Para fazer isso, defina a propriedade do UserActivity objeto respondente e implemente o UpdateUserActivityState método. Consulte a seção Transferência de suporte nos respondentes abaixo para obter mais informações.

Suporte ao App Framework

Tanto UIKit (iOS) quanto AppKit (OS X) fornecem suporte interno para Handoff no NSDocument, Responder (NSResponderUIResponder/) e AppDelegate classes. Embora cada sistema operacional implemente Handoff ligeiramente diferente, o mecanismo básico e as APIs são os mesmos.

Atividades do usuário em aplicativos baseados em documentos

Os aplicativos iOS e OS X baseados em documentos têm automaticamente o suporte a Handoff integrado. Para ativar esse suporte, você precisará adicionar uma chave e um NSUbiquitousDocumentUserActivityType valor para cada CFBundleDocumentTypes entrada no arquivo Info.plist do aplicativo.

Se essa chave estiver presente, crie NSDocument instâncias automaticamente NSUserActivity para UIDocument documentos baseados no iCloud do tipo especificado. Você precisará fornecer um tipo de atividade para cada tipo de documento que o aplicativo oferece suporte e vários tipos de documento podem usar o mesmo tipo de atividade. Ambos NSDocument e preencham UIDocument automaticamente a UserInfo propriedade do com o NSUserActivity valor de sua FileURL propriedade.

No OS X, o NSUserActivity gerenciado por AppKit e associado aos respondentes se torna automaticamente a Atividade Atual quando a janela do documento se torna a janela principal. No iOS, para NSUserActivity objetos gerenciados pelo UIKit, você deve chamar BecomeCurrent o método explicitamente ou ter a propriedade do UserActivity documento definida em um UIViewController quando o aplicativo estiver em primeiro plano.

AppKit irá restaurar automaticamente qualquer UserActivity propriedade criada desta forma no OS X. Isso ocorre se o ContinueUserActivity método retorna false ou se ele não é implementado. Nessa situação, o documento é aberto com o OpenDocument método do e, em seguida, receberá uma RestoreUserActivityState chamada de NSDocumentController método.

Consulte a seção Transferência de suporte em aplicativos baseados em documentos abaixo para obter mais informações.

Atividades do usuário e respondentes

Ambos UIKit e podem gerenciar automaticamente uma atividade do usuário se você defini-la como propriedade de UserActivity um objeto de respondenteAppKit. Se o estado tiver sido modificado, você precisará definir a NeedsSave propriedade do respondente UserActivity como true. O sistema salvará automaticamente o UserActivity quando necessário, depois de dar ao respondente tempo para atualizar o estado chamando seu UpdateUserActivityState método.

Se vários respondentes compartilharem uma única NSUserActivity instância, eles receberão um retorno de UpdateUserActivityState chamada quando o sistema atualizar o objeto de atividade do usuário. O respondente precisa chamar o AddUserInfoEntries método para atualizar o NSUserActivitydicionário do para refletir o estado de UserInfo atividade atual neste ponto. O UserInfo dicionário é limpo antes de cada UpdateUserActivityState chamada.

Para se desassociar de uma atividade, um respondente pode definir sua UserActivity propriedade como null. Quando uma instância gerenciada NSUserActivity da estrutura do aplicativo não tem mais respondentes ou documentos associados, ela é automaticamente invalidada.

Consulte a seção Transferência de suporte nos respondentes abaixo para obter mais informações.

Atividades do usuário e o AppDelegate

O do AppDelegate aplicativo é o ponto de entrada principal ao lidar com uma continuação de Handoff. Quando o usuário responde a uma notificação de Handoff, o aplicativo apropriado é iniciado (se ainda não estiver em execução) e o WillContinueUserActivityWithType método do AppDelegate será chamado. Neste momento, o aplicativo deve informar ao usuário que a continuação está começando.

A NSUserActivity instância é entregue quando o AppDelegatemétodo 's ContinueUserActivity é chamado. Neste ponto, você deve configurar a interface do usuário do aplicativo e continuar a atividade fornecida.

Consulte a seção Implementando Handoff abaixo para obter mais informações.

Ativando o Handoff em um aplicativo Xamarin

Devido aos requisitos de segurança impostos pelo Handoff, um aplicativo Xamarin.iOS que usa a estrutura Handoff deve ser configurado corretamente no Apple Developer Portal e no arquivo de projeto Xamarin.iOS.

Faça o seguinte:

  1. Faça login no Apple Developer Portal.

  2. Clique em Certificados, Identificadores e Perfis.

  3. Se você ainda não tiver feito isso, clique em Identificadores e crie uma ID para seu aplicativo (por exemplo com.company.appname, ), caso contrário, edite sua ID existente.

  4. Certifique-se de que o serviço do iCloud foi verificado para o ID fornecido:

    Ativar o serviço do iCloud para o ID fornecido

  5. Salve suas alterações.

  6. Clique em Desenvolvimento de perfis> de provisionamento e crie um novo perfil de provisionamento de desenvolvimento para seu aplicativo:

    Criar um novo perfil de provisionamento de desenvolvimento para o aplicativo

  7. Baixe e instale o novo perfil de provisionamento ou use o Xcode para baixar e instalar o perfil.

  8. Edite suas opções de projeto Xamarin.iOS e verifique se você está usando o perfil de provisionamento que acabou de criar:

    Selecione o perfil de provisionamento recém-criado

  9. Em seguida, edite o arquivo Info.plist e verifique se você está usando a ID do aplicativo que foi usada para criar o perfil de provisionamento:

    Definir ID do aplicativo

  10. Role até a seção Modos de plano de fundo e verifique os seguintes itens:

    Habilitar os modos de plano de fundo necessários

  11. Salve as alterações em todos os arquivos.

Com essas configurações em vigor, o aplicativo agora está pronto para acessar as APIs do Handoff Framework. Para obter informações detalhadas sobre o provisionamento, consulte nossos guias de provisionamento de dispositivos e provisionamento de seu aplicativo .

Implementando Handoff

As atividades do usuário podem ser continuadas entre aplicativos assinados com a mesma ID da equipe do desenvolvedor e oferecem suporte ao mesmo Tipo de atividade. A implementação do Handoff em um aplicativo Xamarin.iOS requer que você crie um Objeto de Atividade do Usuário (em UIKit ou em ou AppKit), atualize o estado do objeto para rastrear a atividade e continue a atividade em um dispositivo receptor.

Identificando atividades do usuário

A primeira etapa na implementação do Handoff é identificar os tipos de atividades do usuário que seu aplicativo oferece suporte e ver quais dessas atividades são boas candidatas para continuação em outro dispositivo. Por exemplo: um aplicativo ToDo pode oferecer suporte à edição de itens como um Tipo de Atividade do Usuário e oferecer suporte à navegação na lista de itens disponíveis como outro.

Um aplicativo pode criar quantos Tipos de Atividade de Usuário forem necessários, um para qualquer função que o aplicativo fornecer. Para cada Tipo de Atividade do Usuário, o aplicativo precisará controlar quando uma atividade do tipo começa e termina e precisa manter as informações de estado atualizadas para continuar essa tarefa em outro dispositivo.

As Atividades do Usuário podem ser continuadas em qualquer aplicativo assinado com a mesma ID de Equipe sem qualquer mapeamento um-para-um entre os aplicativos de envio e recebimento. Por exemplo, um determinado aplicativo pode criar quatro tipos diferentes de atividades, que são consumidas por aplicativos diferentes e individuais em outro dispositivo. Essa é uma ocorrência comum entre uma versão Mac do aplicativo (que pode ter muitos recursos e funções) e aplicativos iOS, onde cada aplicativo é menor e focado em uma tarefa específica.

Criando identificadores de tipo de atividade

O Identificador de Tipo de Atividade é uma cadeia de caracteres curta adicionada à NSUserActivityTypes matriz do arquivo Info.plist do aplicativo usada para identificar exclusivamente um determinado Tipo de Atividade do Usuário. Haverá uma entrada na matriz para cada atividade que o aplicativo suporta. A Apple sugere o uso de uma notação no estilo DNS reverso para o Identificador de Tipo de Atividade para evitar colisões. Por exemplo: com.company-name.appname.activity para atividades específicas baseadas em aplicativos ou com.company-name.activity para atividades que podem ser executadas em vários aplicativos.

O Identificador de Tipo de Atividade é usado ao criar uma NSUserActivity instância para identificar o tipo de atividade. Quando uma atividade é continuada em outro dispositivo, o Tipo de Atividade (junto com a ID da Equipe do aplicativo) determina qual aplicativo iniciar para continuar a atividade.

Como exemplo, vamos criar um aplicativo de exemplo chamado MonkeyBrowser. Este aplicativo apresentará quatro guias, cada uma com uma URL diferente aberta em uma exibição do navegador da Web. O usuário poderá continuar qualquer guia em um dispositivo iOS diferente executando o aplicativo.

Para criar os identificadores de tipo de atividade necessários para oferecer suporte a esse comportamento, edite o arquivo Info.plist e alterne para o modo de exibição Origem . Adicione uma NSUserActivityTypes chave e crie os seguintes identificadores:

A chave NSUserActivityTypes e os identificadores necessários no editor plist

Criamos quatro novos Identificadores de Tipo de Atividade, um para cada uma das guias no aplicativo MonkeyBrowser de exemplo. Ao criar seus próprios aplicativos, substitua o conteúdo da matriz pelos Identificadores de Tipo de NSUserActivityTypes Atividade específicos para as atividades que seu aplicativo suporta.

Acompanhando alterações na atividade do usuário

Quando criamos uma nova instância da NSUserActivity classe, especificamos uma NSUserActivityDelegate instância para controlar as alterações no estado da atividade. Por exemplo, o código a seguir pode ser usado para controlar alterações 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
    }
}

O UserActivityReceivedData método é chamado quando um fluxo de continuação recebeu dados de um dispositivo de envio. Para obter mais informações, consulte a seção Suportando fluxos de continuação abaixo.

O UserActivityWasContinued método é chamado quando outro dispositivo assumiu uma atividade do dispositivo atual. Dependendo do tipo de atividade, como adicionar um novo item a uma lista de tarefas pendentes, o aplicativo pode precisar interromper a atividade no dispositivo de envio.

O UserActivityWillSave método é chamado antes que quaisquer alterações na atividade sejam salvas e sincronizadas entre dispositivos disponíveis localmente. Você pode usar esse método para fazer alterações de última hora na UserInfo propriedade da instância antes que NSUserActivity ela seja enviada.

Criando uma instância NSUserActivity

Cada atividade que seu aplicativo deseja fornecer a possibilidade de continuar em outro dispositivo deve ser encapsulada em uma NSUserActivity instância. O aplicativo pode criar quantas atividades forem necessárias e a natureza dessas atividades depende da funcionalidade e dos recursos do aplicativo em questão. Por exemplo, um aplicativo de email pode criar uma atividade para criar uma nova mensagem e outra para ler uma mensagem.

Para nosso aplicativo de exemplo, um novo NSUserActivity é criado sempre que o usuário insere uma nova URL em uma das exibições do navegador da Web com guias. O código a seguir armazena o estado de uma determinada guia:

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

Ele cria um novo NSUserActivity usando um dos tipos de atividade de usuário criados acima e fornece um título legível por humanos para a atividade. Ele anexa a uma instância do NSUserActivityDelegate criado acima para observar as alterações de estado e informa ao iOS que essa Atividade do Usuário é a Atividade Atual.

Preenchendo o dicionário UserInfo

Como vimos acima, a UserInfoNSUserActivity propriedade da classe é um NSDictionary dos pares chave-valor usados para definir o estado de uma determinada atividade. Os valores armazenados em UserInfo devem ser de um dos seguintes tipos: NSArray, NSData, NSDate, NSDictionary, NSNull, , NSNumber, NSSet, ou NSStringNSURL. NSURL Os valores de dados que apontam para documentos do iCloud serão ajustados automaticamente para que apontem para os mesmos documentos em um dispositivo receptor.

No exemplo acima, criamos um NSMutableDictionary objeto e o preenchemos com uma única chave fornecendo a URL que o usuário estava visualizando atualmente na guia fornecida. O AddUserInfoEntries método da Atividade do Usuário foi usado para atualizar a atividade com os dados que serão usados para restaurar a atividade no 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);

A Apple sugere manter as informações enviadas ao mínimo para garantir que a atividade seja enviada em tempo hábil para o dispositivo receptor. Se forem necessárias informações maiores, como uma imagem anexada a um documento a ser editado, você deve usar Fluxos de Continuação. Consulte a seção Suportes de Fluxos de Continuação abaixo para obter mais detalhes.

Continuando uma atividade

O Handoff informará automaticamente os dispositivos iOS e OS X locais que estão em proximidade física com o dispositivo de origem e conectados à mesma conta do iCloud, sobre a disponibilidade de Atividades de Usuário contínuas. Se o usuário optar por continuar uma atividade em um novo dispositivo, o sistema iniciará o aplicativo apropriado (com base na ID da Equipe e no Tipo de Atividade) e informará AppDelegate que a continuação precisa ocorrer.

Primeiro, o WillContinueUserActivityWithType método é chamado para que o aplicativo possa informar ao usuário que a continuação está prestes a começar. Usamos o seguinte código no arquivo de AppDelegate.cs de nosso aplicativo de exemplo para lidar com uma continuação iniciando:

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;
}

No exemplo acima, cada View Controller se registra com o AppDelegate e tem um método público PreparingToHandoff que exibe um indicador de atividade e uma mensagem informando ao usuário que a atividade está prestes a ser entregue ao dispositivo atual. Exemplo:

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...");
}

O ContinueUserActivity do AppDelegate será chamado para realmente continuar a atividade dada. Novamente, a partir do nosso aplicativo de exemplo:

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;
}

O método público PerformHandoff de cada View Controller realmente preforma a transferência e restaura a atividade no dispositivo atual. No caso do exemplo, ele exibe a mesma URL em uma determinada guia que o usuário estava navegando em um dispositivo diferente. Exemplo:

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

}

O ContinueUserActivity método inclui um UIApplicationRestorationHandler que você pode chamar para a retomada da atividade baseada em documento ou respondente. Você precisará passar um NSArray ou objetos restauráveis para o manipulador de restauração quando chamado. Por exemplo:

completionHandler (new NSObject[]{Tab4});

Para cada objeto passado, seu RestoreUserActivityState método será chamado. Cada objeto pode usar os dados no UserInfo dicionário para restaurar seu próprio estado. Por exemplo:

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

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

Para aplicativos baseados em documentos, se você não implementar o ContinueUserActivity método ou ele retornar false, UIKit ou AppKit puder retomar automaticamente a atividade. Consulte a seção Transferência de suporte em aplicativos baseados em documentos abaixo para obter mais informações.

Falha na transferência graciosa

Como o Handoff depende da transmissão de informações entre uma coleção de dispositivos iOS e OS X conectados de forma frouxa, o processo de transferência às vezes pode falhar. Você deve projetar seu aplicativo para lidar com essas falhas normalmente e informar o usuário sobre quaisquer situações que surjam.

Em caso de falha, o DidFailToContinueUserActivitiy método do AppDelegate será chamado. Por exemplo:

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);
}

Você deve usar o fornecido NSError para fornecer informações ao usuário sobre a falha.

Transferência de aplicativo nativo para navegador da Web

Um usuário pode querer continuar uma atividade sem ter um aplicativo nativo apropriado instalado no dispositivo desejado. Em algumas situações, uma interface baseada na Web pode fornecer a funcionalidade necessária e a atividade ainda pode ser continuada. Por exemplo, a conta de email do usuário pode fornecer uma interface do usuário baseada na Web para compor e ler mensagens.

Se o aplicativo nativo de origem souber a URL da interface da Web (e a sintaxe necessária para identificar o determinado item que está sendo continuado), ele poderá codificar essas informações na WebpageURL propriedade da NSUserActivity instância. Se o dispositivo de recebimento não tiver um aplicativo nativo apropriado instalado para lidar com a continuação, a interface da Web fornecida poderá ser chamada.

Transferência do navegador da Web para o aplicativo nativo

Se o usuário estava usando uma interface baseada na Web no dispositivo de origem e um aplicativo nativo no dispositivo de recebimento reivindica a parte de domínio da WebpageURL propriedade, o sistema usará esse aplicativo para manipular a continuação. O novo dispositivo receberá uma NSUserActivity instância que marca o Tipo de Atividade como BrowsingWeb e o WebpageURL conterá a URL que o usuário estava visitando, o UserInfo dicionário estará vazio.

Para que um aplicativo participe desse tipo de Handoff, ele deve reivindicar o domínio em um com.apple.developer.associated-domains direito com o formato <service>:<fully qualified domain name> (por exemplo: activity continuation:company.com).

Se o domínio especificado corresponder ao valor de uma WebpageURL propriedade, o Handoff baixará uma lista de IDs de aplicativo aprovados do site nesse domínio. O site deve fornecer uma lista de IDs aprovados em um arquivo JSON assinado chamado apple-app-site-association (por exemplo, https://company.com/apple-app-site-association).

Esse arquivo JSON contém um dicionário que especifica uma lista de IDs de aplicativo no formato <team identifier>.<bundle identifier>. Por exemplo:

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

Para assinar o arquivo JSON (para que ele tenha o correto Content-Type de ), use o aplicativo Terminal e um comando com um certificado e uma openssl chave emitidos por uma autoridade de application/pkcs7-mimecertificação confiável pelo iOS (consulte https://support.apple.com/kb/ht5012 para obter uma lista). Por exemplo:

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

O openssl comando gera um arquivo JSON assinado que você coloca em seu site na URL apple-app-site-association . Por exemplo:

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

O aplicativo receberá qualquer atividade cujo WebpageURL domínio esteja em seu com.apple.developer.associated-domains direito. Apenas os protocolos e https são suportadoshttp, qualquer outro protocolo levantará uma exceção.

Suporte a transferência em aplicativos baseados em documentos

Como dito acima, no iOS e no OS X, os aplicativos baseados em documentos suportarão automaticamente a transferência de documentos baseados no iCloud se o arquivo Info.plist do aplicativo contiver uma CFBundleDocumentTypes chave de NSUbiquitousDocumentUserActivityType. Por exemplo:

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

Neste exemplo, a cadeia de caracteres é um designador de aplicativo DNS reverso com o nome da atividade anexado. Se inseridas dessa forma, as entradas de tipo de atividade não precisam ser repetidas NSUserActivityTypes na matriz do arquivo Info.plist .

O objeto User Activity criado automaticamente (disponível por meio da propriedade do UserActivity documento) pode ser referenciado por outros objetos no aplicativo e usado para restaurar o estado na continuação. Por exemplo, para controlar a seleção de itens e a posição do documento. Você precisa definir essa propriedade activities NeedsSave para true sempre que o estado for alterado e atualizar o UserInfo dicionário no UpdateUserActivityState método.

A UserActivity propriedade pode ser usada a partir de qualquer thread e está em conformidade com o protocolo KVO (key-value observando), para que possa ser usada para manter um documento sincronizado à medida que entra e sai do iCloud. A UserActivity propriedade será invalidada quando o documento for fechado.

Para obter mais informações, consulte a documentação Suporte à atividade do usuário da Apple em Aplicativos baseados em documentos.

Suporte à transferência em respondentes

Você pode associar respondentes (herdados do UIResponder iOS ou NSResponder do OS X) a atividades definindo suas UserActivity propriedades. O sistema salva automaticamente a UserActivity propriedade nos momentos apropriados, chamando o método do UpdateUserActivityState respondente para adicionar dados atuais ao objeto User Activity usando o AddUserInfoEntriesFromDictionary método.

Suporte a fluxos de continuação

Podem ser situações em que a quantidade de informações necessárias para continuar uma atividade não pode ser transferida de forma eficiente pela carga inicial de Handoff. Nessas situações, o aplicativo de recebimento pode estabelecer um ou mais fluxos entre ele e o aplicativo de origem para transferir os dados.

O aplicativo de origem definirá a SupportsContinuationStreamsNSUserActivity propriedade da instância como true. Por exemplo:

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

O aplicativo de recebimento pode então chamar o GetContinuationStreamsNSUserActivity método do em seu AppDelegate para estabelecer o fluxo. Por exemplo:

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;
}

No dispositivo de origem, o Delegado de Atividade do Usuário recebe os fluxos chamando seu DidReceiveInputStream método para fornecer os dados solicitados para continuar a atividade do usuário no dispositivo de retomada.

Você usará um NSInputStream para fornecer acesso somente leitura a dados de fluxo e um NSOutputStream fornecer acesso somente gravação. Os fluxos devem ser usados de forma de solicitação e resposta, em que o aplicativo de recebimento solicita mais dados e o aplicativo de origem os fornece. Para que, os dados gravados no fluxo de saída no dispositivo de origem sejam lidos a partir do fluxo de entrada no dispositivo contínuo e vice-versa.

Mesmo em situações em que o Fluxo de Continuação é necessário, deve haver um mínimo de comunicação de ida e volta entre os dois aplicativos.

Para obter mais informações, consulte a documentação Usando fluxos de continuação da Apple.

Práticas recomendadas de transferência

A implementação bem-sucedida da continuação contínua de uma atividade do usuário via Handoff requer um design cuidadoso devido a todos os vários componentes envolvidos. A Apple sugere adotar as seguintes práticas recomendadas para seus aplicativos habilitados para Handoff:

  • Projete suas Atividades de Usuário para exigir a menor carga útil possível para relacionar o estado da atividade a ser continuada. Quanto maior a carga útil, mais tempo leva para iniciar a continuação.
  • Se você precisar transferir grandes quantidades de dados para uma continuidade bem-sucedida, leve em consideração os custos envolvidos na configuração e na sobrecarga de rede.
  • É comum que um aplicativo Mac grande crie Atividades do Usuário que são manipuladas por vários, menores e específicos de tarefas em dispositivos iOS. As diferentes versões do aplicativo e do sistema operacional devem ser projetadas para funcionar bem juntas ou falhar normalmente.
  • Ao especificar seus tipos de atividade, use a notação DNS reverso para evitar colisões. Se uma atividade for específica de um determinado aplicativo, seu nome deverá ser incluído na definição de tipo (por exemplo com.myCompany.myEditor.editing). Se a atividade puder funcionar em vários aplicativos, remova o nome do aplicativo da definição (por exemplo com.myCompany.editing, ).
  • Se seu aplicativo precisar atualizar o estado de uma Atividade do Usuário (NSUserActivity), defina a NeedsSave propriedade como true. Em momentos apropriados, o Handoff chamará o método do UserActivityWillSave delegado para que você possa atualizar o UserInfo dicionário conforme necessário.
  • Como o processo de Handoff pode não inicializar instantaneamente no dispositivo receptor, você deve implementar o AppDelegates WillContinueUserActivity e informar ao usuário que uma continuação está prestes a começar.

Exemplo de aplicativo de transferência

Um exemplo de uso do Handoff em um aplicativo Xamarin.iOS é o aplicativo de exemplo MonkeyBrowser . O aplicativo tem quatro abas que o usuário pode usar para navegar na web, cada uma com um determinado tipo de atividade: Clima, Favorito, Coffee Break e Trabalho.

Em qualquer guia, quando o usuário insere uma nova URL e toca no botão Ir , uma nova NSUserActivity é criada para essa guia que contém a URL que o usuário está navegando no momento:

Exemplo de aplicativo de transferência

Se outro dos dispositivos do usuário tiver o aplicativo MonkeyBrowser instalado, estiver conectado ao iCloud usando a mesma conta de usuário, estiver na mesma rede e próximo ao dispositivo acima, a Atividade de transferência será exibida na tela inicial (no canto inferior esquerdo):

A Atividade de Handoff exibida na tela inicial no canto inferior esquerdo

Se o usuário arrastar para cima no ícone Handoff, o aplicativo será iniciado e a Atividade do Usuário especificada no NSUserActivity será continuada no novo dispositivo:

A atividade do usuário continuou no novo dispositivo

Quando a Atividade do Usuário tiver sido enviada com êxito para outro dispositivo Apple, o dispositivo remetente NSUserActivity receberá uma chamada para o UserActivityWasContinued método nele NSUserActivityDelegate para informá-lo de que a Atividade do Usuário foi transferida com êxito para outro dispositivo.

Resumo

Este artigo deu uma introdução à estrutura Handoff usada para continuar uma atividade do usuário entre vários dispositivos Apple do usuário. Em seguida, ele mostrou como habilitar e implementar o Handoff em um aplicativo Xamarin.iOS. Finalmente, discutiu os diferentes tipos de continuações de Handoff disponíveis e as melhores práticas de Handoff.