Condividi tramite


Consegna in Xamarin.iOS

Questo articolo illustra l'uso di Handoff in un'app Xamarin.iOS per trasferire le attività utente tra app in esecuzione negli altri dispositivi dell'utente.

Apple ha introdotto Handoff in iOS 8 e OS X Yosemite (10.10) per fornire un meccanismo comune per consentire all'utente di trasferire le attività avviate in uno dei dispositivi, in un altro dispositivo che esegue la stessa app o un'altra app che supporta la stessa attività.

Esempio di esecuzione di un'operazione handoff

Questo articolo illustra in dettaglio l'abilitazione della condivisione delle attività in un'app Xamarin.iOS e illustra in dettaglio il framework Handoff:

Informazioni su Handoff

Handoff (noto anche come Continuità) è stato introdotto da Apple in iOS 8 e OS X Yosemite (10.10) come modo per consentire all'utente di avviare un'attività su uno dei propri dispositivi (iOS o Mac) e continuare la stessa attività su un altro dei propri dispositivi (come identificato dall'account iCloud dell'utente).

Handoff è stato ampliato in iOS 9 per supportare anche nuove funzionalità di ricerca avanzate. Per altre informazioni, vedere la documentazione relativa ai miglioramenti della ricerca.

Ad esempio, l'utente può avviare un messaggio di posta elettronica sul proprio i Telefono e continuare facilmente l'e-mail sul proprio Mac, con tutte le stesse informazioni sul messaggio compilate e il cursore nella stessa posizione in cui l'hanno lasciata in iOS.

Tutte le tue app che condividono lo stesso ID team sono idonee per l'uso di Handoff per continuare le attività degli utenti tra le app, purché queste app vengano recapitate tramite iTunes App Store o firmate da uno sviluppatore registrato (per Mac, Enterprise o app ad hoc).

Tutte NSDocument le app basate su o UIDocument includono automaticamente il supporto handoff predefinito e richiedono modifiche minime per supportare handoff.

Continuare le attività degli utenti

La NSUserActivity classe (insieme ad alcune piccole modifiche a UIKit e AppKit) fornisce il supporto per la definizione dell'attività di un utente che può potenzialmente essere continuata in un altro dei dispositivi dell'utente.

Affinché un'attività venga passata a un altro dei dispositivi dell'utente, deve essere incapsulata in un'istanza NSUserActivity, contrassegnata come attività corrente, avere il payload impostato (i dati usati per eseguire la continuazione) e l'attività deve quindi essere trasmessa a tale dispositivo.

L'handoff passa il minimo di informazioni per definire l'attività da continuare, con pacchetti di dati più grandi sincronizzati tramite iCloud.

Nel dispositivo ricevente, l'utente riceverà una notifica che informa che un'attività è disponibile per la continuazione. Se l'utente sceglie di continuare l'attività nel nuovo dispositivo, l'app specificata viene avviata (se non è già in esecuzione) e il payload da NSUserActivity viene usato per riavviare l'attività.

Panoramica delle attività utente continue

Solo le app che condividono lo stesso ID del team di sviluppo e rispondono a un determinato tipo di attività sono idonee per la continuazione. Un'app definisce i tipi di attività supportati sotto la NSUserActivityTypes chiave del relativo file Info.plist . Dato questo, un dispositivo continuo sceglie l'app per eseguire la continuazione in base all'ID team, al tipo di attività e, facoltativamente, al titolo dell'attività.

L'app ricevente usa le informazioni del NSUserActivitydizionario del UserInfo per configurare l'interfaccia utente e ripristinare lo stato dell'attività specificata in modo che la transizione venga visualizzata senza problemi all'utente finale.

Se la continuazione richiede più informazioni di quanto possa essere inviato in modo efficiente tramite un NSUserActivity, l'app di ripresa può inviare una chiamata all'app di origine e stabilire uno o più flussi per trasmettere i dati necessari. Ad esempio, se l'attività stava modificando un documento di testo di grandi dimensioni con più immagini, lo streaming sarebbe necessario per trasferire le informazioni necessarie per continuare l'attività nel dispositivo ricevente. Per altre informazioni, vedere la sezione Support Continuation Flussi di seguito.

Come indicato in precedenza, NSDocument le app basate su o UIDocument includono automaticamente il supporto handoff predefinito. Per altre informazioni, vedere la sezione Supporto delle app basate su documenti di seguito.

Classe NSUserActivity

La NSUserActivity classe è l'oggetto primario in uno scambio handoff e viene usato per incapsulare lo stato di un'attività utente disponibile per la continuazione. Un'app crea un'istanza di una copia di NSUserActivity per qualsiasi attività supportata e vuole continuare su un altro dispositivo. Ad esempio, l'editor di documenti crea un'attività per ogni documento attualmente aperto. Tuttavia, solo il documento più anteriore (visualizzato nella finestra o nella scheda anteriore) è l'attività corrente e disponibile per la continuazione.

Un'istanza di NSUserActivity viene identificata dalle relative ActivityType proprietà e Title . La UserInfo proprietà del dizionario viene utilizzata per contenere informazioni sullo stato dell'attività. Impostare la NeedsSave proprietà su true se si desidera eseguire il caricamento differita delle informazioni sullo stato tramite il NSUserActivitydelegato del . Usare il AddUserInfoEntries metodo per unire nuovi dati da altri client nel UserInfo dizionario in base alle esigenze per mantenere lo stato dell'attività.

Classe NSUserActivityDelegate

Viene NSUserActivityDelegate utilizzato per mantenere aggiornate le informazioni in un NSUserActivityUserInfo dizionario e sincronizzate con lo stato corrente dell'attività. Quando il sistema deve aggiornare le informazioni nell'attività , ad esempio prima della continuazione in un altro dispositivo, chiama il UserActivityWillSave metodo del delegato.

Sarà necessario implementare il UserActivityWillSave metodo e apportare eventuali modifiche a NSUserActivity (ad esempio UserInfo, Titlee così via) per assicurarsi che rifletta ancora lo stato dell'attività corrente. Quando il sistema chiama il UserActivityWillSave metodo , il NeedsSave flag verrà cancellato. Se si modifica una delle proprietà dei dati dell'attività, sarà necessario impostare NeedsSave di nuovo su true .

Invece di usare il UserActivityWillSave metodo presentato in precedenza, è possibile avere UIKit o AppKit gestire automaticamente l'attività dell'utente. A tale scopo, impostare la proprietà dell'oggetto UserActivity risponditore e implementare il UpdateUserActivityState metodo . Per altre informazioni, vedere la sezione Supporto handoff nei risponditori di seguito.

Supporto di App Framework

Sia (iOS) che UIKitAppKit (OS X) forniscono il supporto predefinito per Handoff nelle NSDocumentclassi , Responder (UIResponder/NSResponder) e AppDelegate . Mentre ogni sistema operativo implementa handoff leggermente diversamente, il meccanismo di base e le API sono gli stessi.

Attività utente nelle app basate su documenti

Le app iOS e OS X basate su documenti includono automaticamente il supporto handoff predefinito. Per attivare questo supporto, dovrai aggiungere una chiave e un NSUbiquitousDocumentUserActivityType valore per ogni CFBundleDocumentTypes voce nel file Info.plist dell'app.

Se questa chiave è presente, entrambe NSDocument e UIDocument automaticamente creano NSUserActivity istanze per i documenti basati su iCloud del tipo specificato. Dovrai fornire un tipo di attività per ogni tipo di documento supportato dall'app e più tipi di documento possono usare lo stesso tipo di attività. Sia NSDocument che UIDocument popolano automaticamente la UserInfo proprietà di con il NSUserActivity valore della proprietà FileURL .

In OS X, l'oggetto NSUserActivity gestito da AppKit e associato ai risponditori diventa automaticamente l'attività corrente quando la finestra del documento diventa la finestra principale. In iOS, per NSUserActivity gli oggetti gestiti da UIKit, è necessario chiamare BecomeCurrent il metodo in modo esplicito o impostare la proprietà del UserActivity documento su un UIViewController quando l'app si trova in primo piano.

AppKit ripristina automaticamente qualsiasi UserActivity proprietà creata in questo modo in OS X. Ciò si verifica se il ContinueUserActivity metodo restituisce false o se è non implementato. In questo caso, il documento viene aperto con il OpenDocument metodo di NSDocumentController e riceverà quindi una RestoreUserActivityState chiamata al metodo .

Per altre informazioni, vedere la sezione Supporto delle app basate su documenti più avanti.

Attività e risponditori utente

Sia UIKit che AppKit possono gestire automaticamente un'attività utente se la si imposta come proprietà dell'oggetto UserActivity risponditore. Se lo stato è stato modificato, sarà necessario impostare la NeedsSave proprietà del risponditore UserActivity su true. Il sistema salverà automaticamente quando UserActivity necessario, dopo aver dato al risponditore il tempo necessario per aggiornare lo stato chiamando il relativo UpdateUserActivityState metodo.

Se più risponditori condividono una singola NSUserActivity istanza, ricevono un UpdateUserActivityState callback quando il sistema aggiorna l'oggetto attività utente. Il risponditore deve chiamare il metodo per aggiornare il AddUserInfoEntriesNSUserActivitydizionario per UserInfo riflettere lo stato dell'attività corrente a questo punto. Il UserInfo dizionario viene cancellato prima di ogni UpdateUserActivityState chiamata.

Per annullare l'associazione da un'attività, un risponditore può impostare la relativa UserActivity proprietà su null. Quando un'istanza gestita del framework dell'app NSUserActivity non ha più risponditori o documenti associati, viene invalidata automaticamente.

Per altre informazioni, vedere la sezione Supporto handoff nei risponditori di seguito.

Attività utente e AppDelegate

L'app AppDelegate è il punto di ingresso principale quando si gestisce una continuazione handoff. Quando l'utente risponde a una notifica handoff, l'app appropriata viene avviata (se non è già in esecuzione) e verrà chiamato il WillContinueUserActivityWithType metodo di AppDelegate . A questo punto, l'app deve informare l'utente che la continuazione è in corso.

L'istanza NSUserActivity viene recapitata quando viene chiamato il AppDelegatemetodo .ContinueUserActivity A questo punto, è necessario configurare l'interfaccia utente dell'app e continuare l'attività specificata.

Per altre informazioni, vedere la sezione Implementazione handoff di seguito.

Abilitazione dell'handoff in un'app Xamarin

A causa dei requisiti di sicurezza imposti da Handoff, un'app Xamarin.iOS che usa il framework Handoff deve essere configurata correttamente sia nel portale per sviluppatori Apple che nel file di progetto Xamarin.iOS.

Effettua le operazioni seguenti:

  1. Accedere al portale per sviluppatori Apple.

  2. Fare clic su Certificati, Identificatori e profili.

  3. Se non è già stato fatto, fare clic su Identificatori e creare un ID per l'app ( ad esempio com.company.appname), altrimenti modificare l'ID esistente.

  4. Verificare che il servizio iCloud sia stato controllato per l'ID specificato:

    Abilitare il servizio iCloud per l'ID specificato

  5. Salva le modifiche.

  6. Fare clic su Provisioning Profiles Development (Sviluppo profili>di provisioning) e creare un nuovo profilo di provisioning di sviluppo per l'app:

    Creare un nuovo profilo di provisioning di sviluppo per l'app

  7. Scaricare e installare il nuovo profilo di provisioning o usare Xcode per scaricare e installare il profilo.

  8. Modificare le opzioni del progetto Xamarin.iOS e assicurarsi di usare il profilo di provisioning appena creato:

    Selezionare il profilo di provisioning appena creato

  9. Modificare quindi il file Info.plist e assicurarsi di usare l'ID app usato per creare il profilo di provisioning:

    Impostare l'ID app

  10. Scorrere fino alla sezione Modalità di sfondo e controllare gli elementi seguenti:

    Abilitare le modalità in background necessarie

  11. Salvare le modifiche apportate a tutti i file.

Con queste impostazioni, l'applicazione è ora pronta per accedere alle API handoff framework. Per informazioni dettagliate sul provisioning, vedere le guide Device Provisioning e Provisioning dell'app .

Implementazione dell'handoff

Le attività degli utenti possono essere continuate tra le app firmate con lo stesso ID team per sviluppatori e supportano lo stesso tipo di attività. L'implementazione di Handoff in un'app Xamarin.iOS richiede di creare un oggetto attività utente (in UIKit o AppKit), aggiornare lo stato dell'oggetto per tenere traccia dell'attività e continuare l'attività in un dispositivo ricevente.

Identificazione delle attività utente

Il primo passaggio nell'implementazione di Handoff consiste nell'identificare i tipi di attività utente supportate dall'app e vedere quali di queste attività sono buoni candidati per la continuazione in un altro dispositivo. Ad esempio, un'app ToDo potrebbe supportare la modifica di elementi come tipo di attività utente e supportare l'esplorazione dell'elenco di elementi disponibili come un altro.

Un'app può creare tutti i tipi di attività utente necessari, una per qualsiasi funzione fornita dall'app. Per ogni tipo di attività utente, l'app dovrà tenere traccia dell'inizio e della fine di un'attività del tipo e dovrà mantenere informazioni aggiornate sullo stato per continuare l'attività in un altro dispositivo.

Le attività utente possono essere continuate su qualsiasi app firmata con lo stesso ID team senza alcun mapping uno-a-uno tra l'invio e la ricezione delle app. Ad esempio, una determinata app può creare quattro diversi tipi di attività, utilizzate da app diverse e singole app in un altro dispositivo. Si tratta di un'occorrenza comune tra una versione Mac dell'app (che potrebbe avere molte funzionalità e funzioni) e le app iOS, in cui ogni app è più piccola e incentrata su un'attività specifica.

Creazione di identificatori del tipo di attività

L'identificatoredel tipo di attività è una stringa breve aggiunta alla NSUserActivityTypes matrice del file Info.plist dell'app usata per identificare in modo univoco un determinato tipo di attività utente. Nella matrice sarà presente una voce per ogni attività supportata dall'app. Apple suggerisce di usare una notazione in stile DNS inversa per l'identificatore del tipo di attività per evitare conflitti. Ad esempio: com.company-name.appname.activity per attività specifiche basate su app o com.company-name.activity per attività che possono essere eseguite tra più app.

L'identificatore del tipo di attività viene usato durante la creazione di un'istanza NSUserActivity per identificare il tipo di attività. Quando un'attività viene continuata in un altro dispositivo, il tipo di attività (insieme all'ID team dell'app) determina l'app da avviare per continuare l'attività.

Ad esempio, creeremo un'app di esempio denominata MonkeyBrowser. Questa app presenterà quattro schede, ognuna con un URL diverso aperto in una visualizzazione del Web browser. L'utente sarà in grado di continuare qualsiasi scheda in un dispositivo iOS diverso che esegue l'app.

Per creare gli identificatori del tipo di attività necessari per supportare questo comportamento, modificare il file Info.plist e passare alla visualizzazione Origine . Aggiungere una NSUserActivityTypes chiave e creare gli identificatori seguenti:

Chiave NSUserActivityTypes e identificatori obbligatori nell'editor plist

Sono stati creati quattro nuovi identificatori del tipo di attività, uno per ognuna delle schede nell'app MonkeyBrowser di esempio. Quando si creano app personalizzate, sostituire il contenuto della NSUserActivityTypes matrice con gli identificatori del tipo di attività specifici per le attività supportate dall'app.

Rilevamento delle modifiche delle attività utente

Quando si crea una nuova istanza della NSUserActivity classe , verrà specificata un'istanza NSUserActivityDelegate per tenere traccia delle modifiche apportate allo stato dell'attività. Ad esempio, il codice seguente può essere usato per tenere traccia delle modifiche dello stato:

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

Il UserActivityReceivedData metodo viene chiamato quando un flusso di continuazione ha ricevuto dati da un dispositivo di invio. Per altre informazioni, vedere la sezione Support Continuation Flussi di seguito.

Il UserActivityWasContinued metodo viene chiamato quando un altro dispositivo ha acquisito un'attività dal dispositivo corrente. A seconda del tipo di attività, ad esempio l'aggiunta di un nuovo elemento a un elenco ToDo, l'app potrebbe dover interrompere l'attività nel dispositivo di invio.

Il UserActivityWillSave metodo viene chiamato prima che le modifiche apportate all'attività vengano salvate e sincronizzate tra i dispositivi disponibili in locale. È possibile utilizzare questo metodo per apportare modifiche dell'ultimo minuto alla UserInfo proprietà dell'istanza prima dell'invio NSUserActivity .

Creazione di un'istanza NSUserActivity

Ogni attività che l'app vuole fornire la possibilità di continuare in un altro dispositivo deve essere incapsulata in un'istanza NSUserActivity di . L'app può creare tutte le attività necessarie e la natura di tali attività dipende dalle funzionalità e dalle funzionalità dell'app in questione. Ad esempio, un'app di posta elettronica potrebbe creare un'attività per la creazione di un nuovo messaggio e un'altra per la lettura di un messaggio.

Per l'app di esempio, viene creato un nuovo NSUserActivity oggetto ogni volta che l'utente immette un nuovo URL in una delle visualizzazioni del Web browser a schede. Il codice seguente archivia lo stato di una determinata scheda:

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

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

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

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

Crea un nuovo NSUserActivity oggetto usando uno dei tipi di attività utente creati in precedenza e fornisce un titolo leggibile per l'attività. Viene collegato a un'istanza dell'oggetto NSUserActivityDelegate creato in precedenza per controllare le modifiche di stato e informa iOS che questa attività utente è l'attività corrente.

Popolamento del dizionario UserInfo

Come illustrato in precedenza, la UserInfo proprietà della NSUserActivity classe è una NSDictionary delle coppie chiave-valore usate per definire lo stato di una determinata attività. I valori archiviati in UserInfo devono essere uno dei tipi seguenti: NSArray, , NSDataNSDate, NSDictionary, NSSetNSStringNSNullNSNumbero .NSURL NSURL i valori dei dati che puntano ai documenti iCloud verranno modificati automaticamente in modo che puntino agli stessi documenti in un dispositivo ricevente.

Nell'esempio precedente è stato creato un NSMutableDictionary oggetto e lo è stato popolato con una singola chiave che fornisce l'URL attualmente visualizzato dall'utente nella scheda specificata. Il AddUserInfoEntries metodo dell'attività utente è stato usato per aggiornare l'attività con i dati che verranno usati per ripristinare l'attività nel dispositivo ricevente:

// 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 suggerisce di mantenere le informazioni inviate al minimo minimo per garantire che l'attività venga inviata in modo tempestivo al dispositivo ricevente. Se sono necessarie informazioni più grandi, ad esempio un'immagine associata a un documento da modificare, è necessario usare il Flussi continuazione. Per altri dettagli, vedere la sezione Flussi di supporto della continuazione di seguito.

Continuazione di un'attività

Handoff informerà automaticamente i dispositivi iOS e OS X locali che si trovano in prossimità fisica al dispositivo di origine e hanno eseguito l'accesso allo stesso account iCloud, della disponibilità delle attività utente continuabili. Se l'utente sceglie di continuare un'attività in un nuovo dispositivo, il sistema avvierà l'app appropriata (in base all'ID team e al tipo di attività) e le informazioni AppDelegate necessarie per la continuazione.

In primo luogo, il WillContinueUserActivityWithType metodo viene chiamato in modo che l'app possa informare l'utente che la continuazione sta per iniziare. Nel file di AppDelegate.cs dell'app di esempio viene usato il codice seguente per gestire un'avvio di continuazione:

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

Nell'esempio precedente ogni controller di visualizzazione viene registrato con AppDelegate e ha un metodo pubblico PreparingToHandoff che visualizza un indicatore attività e un messaggio che informa l'utente che l'attività sta per essere passata al dispositivo corrente. Esempio:

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

L'oggetto ContinueUserActivity dell'oggetto AppDelegate verrà chiamato per continuare effettivamente l'attività specificata. Anche in questo caso, dall'app di esempio:

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

Il metodo pubblico PerformHandoff di ogni controller di visualizzazione preforma effettivamente l'handoff e ripristina l'attività nel dispositivo corrente. Nel caso dell'esempio, viene visualizzato lo stesso URL in una determinata scheda in cui l'utente stava esplorando un dispositivo diverso. Esempio:

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

}

Il ContinueUserActivity metodo include un UIApplicationRestorationHandler oggetto che è possibile chiamare per la ripresa dell'attività basata su documento o risponditore. È necessario passare un NSArray oggetto o ripristinabile al gestore di ripristino quando viene chiamato. Ad esempio:

completionHandler (new NSObject[]{Tab4});

Per ogni oggetto passato, verrà chiamato il relativo RestoreUserActivityState metodo. Ogni oggetto può quindi usare i dati nel UserInfo dizionario per ripristinare il proprio stato. Ad esempio:

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

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

Per le app basate su documenti, se non si implementa il ContinueUserActivity metodo o restituisce falseo AppKitUIKit può riprendere automaticamente l'attività. Per altre informazioni, vedere la sezione Supporto delle app basate su documenti più avanti.

Errore handoff normalmente

Poiché Handoff si basa sulla trasmissione di informazioni tra una raccolta di dispositivi iOS e OS X con connessione libera, il processo di trasferimento può talvolta non riuscire. È consigliabile progettare l'app per gestire questi errori in modo normale e informare l'utente di eventuali situazioni che si verificano.

In caso di errore, verrà chiamato il DidFailToContinueUserActivitiy metodo di AppDelegate . Ad esempio:

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

È consigliabile usare l'oggetto fornito NSError per fornire informazioni all'utente sull'errore.

Handoff da app nativa a Web browser

Un utente può voler continuare un'attività senza che nel dispositivo desiderato sia installata un'app nativa appropriata. In alcune situazioni, un'interfaccia basata sul Web può fornire la funzionalità necessaria e l'attività può comunque essere continuata. Ad esempio, l'account di posta elettronica dell'utente può fornire un'interfaccia utente di base Web per la composizione e la lettura dei messaggi.

Se l'app nativa conosce l'URL dell'interfaccia Web (e la sintassi necessaria per identificare l'elemento specificato in corso), può codificare queste informazioni nella WebpageURL proprietà dell'istanza NSUserActivity . Se nel dispositivo ricevente non è installata un'app nativa appropriata per gestire la continuazione, è possibile chiamare l'interfaccia Web fornita.

Handoff da Web browser a app nativa

Se l'utente usa un'interfaccia basata sul Web nel dispositivo di origine e un'app nativa nel dispositivo ricevente dichiara la parte di dominio della WebpageURL proprietà, il sistema userà tale app per gestire la continuazione. Il nuovo dispositivo riceverà un'istanza NSUserActivity che contrassegna il tipo di attività come BrowsingWeb e WebpageURL conterrà l'URL visitato dall'utente, il UserInfo dizionario sarà vuoto.

Affinché un'app partecipi a questo tipo di handoff, deve richiedere il dominio in un com.apple.developer.associated-domains diritto con il formato <service>:<fully qualified domain name> (ad esempio: activity continuation:company.com).

Se il dominio specificato corrisponde al valore di una WebpageURL proprietà, Handoff scarica un elenco di ID app approvati dal sito Web in quel dominio. Il sito Web deve fornire un elenco di ID approvati in un file JSON firmato denominato apple-app-site-association (ad esempio, https://company.com/apple-app-site-association).

Questo file JSON contiene un dizionario che specifica un elenco di ID app nel formato <team identifier>.<bundle identifier>. Ad esempio:

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

Per firmare il file JSON (in modo che abbia il valore corretto Content-Type di application/pkcs7-mime), usare l'app Terminale e un comando con un certificato e una openssl chiave rilasciati da un'autorità di certificazione attendibile da iOS (vedere https://support.apple.com/kb/ht5012 per un elenco). Ad esempio:

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

Il openssl comando restituisce un file JSON firmato inserito nel sito Web all'URL apple-app-site-association . Ad esempio:

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

L'app riceverà qualsiasi attività il cui WebpageURL dominio è nel suo com.apple.developer.associated-domains diritto. Solo i http protocolli e https sono supportati, qualsiasi altro protocollo genererà un'eccezione.

Supporto dell'handoff nelle app basate su documenti

Come indicato in precedenza, nelle app basate su iOS e OS X, le app basate su documenti supporteranno automaticamente handoff di documenti basati su iCloud se il file Info.plist dell'app contiene una CFBundleDocumentTypes chiave di NSUbiquitousDocumentUserActivityType. Ad esempio:

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

In questo esempio la stringa è un designatore di app DNS inverso con il nome dell'attività aggiunta. Se immesso in questo modo, le voci del tipo di attività non devono essere ripetute nella NSUserActivityTypes matrice del file Info.plist .

L'oggetto Attività utente creato automaticamente (disponibile tramite la proprietà del UserActivity documento) può essere fatto riferimento da altri oggetti nell'app e usati per ripristinare lo stato durante la continuazione. Ad esempio, per tenere traccia della selezione degli elementi e della posizione del documento. È necessario impostare questa proprietà delle attività NeedsSave su true ogni volta che lo stato cambia e aggiornare il UserInfo dizionario nel UpdateUserActivityState metodo .

La UserActivity proprietà può essere usata da qualsiasi thread e conforme al protocollo KVO (key-value observing), in modo che possa essere usato per mantenere un documento sincronizzato quando si sposta e fuori da iCloud. La UserActivity proprietà verrà invalidata quando il documento viene chiuso.

Per altre informazioni, vedere la documentazione relativa al supporto delle attività utente di Apple nella documentazione delle app basate su documenti.

Supporto dell'handoff nei risponditori

È possibile associare i risponditori (ereditati da UIResponder in iOS o NSResponder in OS X) alle attività impostandone UserActivity le proprietà. Il sistema salva automaticamente la UserActivity proprietà nei momenti appropriati, chiamando il metodo del UpdateUserActivityState risponditore per aggiungere dati correnti all'oggetto User Activity usando il AddUserInfoEntriesFromDictionary metodo .

Supporto della Flussi di continuazione

Potrebbe essere una situazione in cui la quantità di informazioni necessarie per continuare un'attività non può essere trasferita in modo efficiente dal payload iniziale handoff. In queste situazioni, l'app ricevente può stabilire uno o più flussi tra se stesso e l'app di origine per trasferire i dati.

L'app di origine imposta la SupportsContinuationStreams proprietà dell'istanza NSUserActivity su true. Ad esempio:

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

L'app ricevente può quindi chiamare il GetContinuationStreams metodo di NSUserActivity in per AppDelegate stabilire il flusso. Ad esempio:

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

Nel dispositivo di origine, il delegato dell'attività utente riceve i flussi chiamando il relativo DidReceiveInputStream metodo per fornire i dati richiesti per continuare l'attività dell'utente nel dispositivo di ripresa.

Si userà un NSInputStream oggetto per fornire l'accesso in sola lettura ai dati di flusso e un NSOutputStream accesso di sola scrittura. I flussi devono essere usati in modo di richiesta e risposta, in cui l'app ricevente richiede più dati e l'app di origine la fornisce. In questo modo, i dati scritti nel flusso di output nel dispositivo di origine vengono letti dal flusso di input nel dispositivo continuo e viceversa.

Anche in situazioni in cui è necessario il flusso di continuazione, dovrebbe esserci una comunicazione minima tra le due app.

Per altre informazioni, vedere la documentazione relativa all'uso della continuazione di Apple Flussi.

Procedure consigliate per l'handoff

L'implementazione corretta della continuazione senza interruzioni di un'attività utente tramite Handoff richiede un'attenta progettazione a causa di tutti i vari componenti coinvolti. Apple suggerisce di adottare le procedure consigliate seguenti per le app abilitate per handoff:

  • Progettare le attività utente per richiedere il payload più piccolo possibile per correlare lo stato dell'attività da continuare. Maggiore è il payload, più tempo richiede l'avvio della continuazione.
  • Se è necessario trasferire grandi quantità di dati per una continuazione corretta, tenere conto dei costi correlati alla configurazione e al sovraccarico di rete.
  • È comune per un'app Mac di grandi dimensioni creare attività utente gestite da diverse app più piccole e specifiche delle attività nei dispositivi iOS. Le diverse versioni dell'app e del sistema operativo devono essere progettate per funzionare bene insieme o non riuscire correttamente.
  • Quando si specificano i tipi di attività, usare la notazione DNS inversa per evitare conflitti. Se un'attività è specifica di una determinata app, il nome deve essere incluso nella definizione del tipo ( ad esempio com.myCompany.myEditor.editing). Se l'attività può funzionare in più app, eliminare il nome dell'app dalla definizione ( ad esempio com.myCompany.editing).
  • Se l'app deve aggiornare lo stato di un'attività utente (NSUserActivity) impostare la NeedsSave proprietà su true. In momenti appropriati, Handoff chiamerà il metodo del UserActivityWillSave delegato in modo da poter aggiornare il UserInfo dizionario in base alle esigenze.
  • Poiché il processo handoff potrebbe non inizializzare immediatamente sul dispositivo ricevente, è necessario implementare e AppDelegateWillContinueUserActivity informare l'utente che una continuazione sta per iniziare.

Esempio di app handoff

Un esempio di uso di Handoff in un'app Xamarin.iOS è l'app di esempio MonkeyBrowser . L'app ha quattro schede che l'utente può usare per esplorare il Web, ognuna con un determinato tipo di attività: Meteo, Preferito, Pausa caffè e Lavoro.

In qualsiasi scheda, quando l'utente immette un nuovo URL e tocca il pulsante Vai , viene creato un nuovo NSUserActivity elemento per tale scheda contenente l'URL attualmente esplorato dall'utente:

Esempio di app handoff

Se un altro dispositivo dell'utente ha installato l'app MonkeyBrowser , è connesso a iCloud usando lo stesso account utente, si trova nella stessa rete e in prossimità del dispositivo precedente, l'attività handoff verrà visualizzata nella schermata iniziale (nell'angolo inferiore sinistro):

Attività handoff visualizzata nella schermata iniziale nell'angolo inferiore sinistro

Se l'utente trascina verso l'alto sull'icona Handoff, l'app verrà avviata e l'attività utente specificata in NSUserActivity verrà continuata nel nuovo dispositivo:

L'attività utente ha continuato nel nuovo dispositivo

Quando l'attività utente è stata inviata correttamente a un altro dispositivo Apple, il dispositivo di NSUserActivity invio riceverà una chiamata al UserActivityWasContinued metodo per NSUserActivityDelegate informarlo che l'attività utente è stata trasferita correttamente a un altro dispositivo.

Riepilogo

Questo articolo ha fornito un'introduzione al framework Handoff usato per continuare un'attività utente tra più dispositivi Apple dell'utente. Successivamente, ha illustrato come abilitare e implementare Handoff in un'app Xamarin.iOS. Infine, sono stati illustrati i diversi tipi di continuazioni Handoff disponibili e le procedure consigliate per handoff.