Xamarin.iOS 中的交接

本文涵蓋在 Xamarin.iOS 應用程式中使用 Handoff,以在使用者的其他裝置上執行的應用程式之間傳輸用戶活動。

Apple 在 iOS 8 和 OS X Yosemite (10.10) 中引進了 Handoff,為使用者提供一種通用機制,讓使用者將啟動的活動傳輸至執行相同應用程式的另一個裝置,或支援相同活動的另一個應用程式。

執行 Handoff 作業的範例

本文將快速探討如何在 Xamarin.iOS 應用程式中啟用活動共用,並詳細討論 Handoff 架構:

關於交接

交接(也稱為 Continuity)是由 Apple 在 iOS 8 和 OS X Yosemite (10.10) 中引進的,這是使用者在其其中一部裝置上啟動活動的方式(iOS 或 Mac),並在另一部裝置上繼續相同的活動(如使用者的 iCloud 帳戶所識別)。

iOS 9 中擴充了交接功能,也支援新的增強型搜尋功能。 如需詳細資訊,請參閱我們的 搜尋增強功能 檔。

例如,用戶可以在其 i 電話 上啟動電子郵件,並在 Mac 上順暢地繼續電子郵件,並填入所有相同的郵件資訊,並在他們將其留在 iOS 中的相同位置游標。

任何共用相同 小組標識碼 的應用程式都有資格使用 Handoff 繼續跨應用程式的用戶活動,只要這些應用程式是透過 iTunes App Store 傳遞,或由註冊的開發人員簽署(適用於 Mac、企業或臨機操作應用程式)。

任何 NSDocumentUIDocument 架構的應用程式都會自動內建 Handoff 支援,而且需要最少的變更來支援 Handoff。

繼續用戶活動

類別NSUserActivity(以及和AppKit的一些小變更UIKit)提供定義用戶活動的支持,這些活動可能會繼續於另一個使用者的裝置上。

若要將活動傳遞至另一個使用者的裝置,它必須封裝在實例 NSUserActivity中,標示為 「目前活動」,其承載集(用來執行接續的數據),然後活動必須傳送至該裝置。

交接會傳遞最少的資訊,以定義要繼續的活動,並透過iCloud同步處理較大的數據封包。

在接收裝置上,使用者會收到活動可供接續的通知。 如果使用者選擇在新裝置上繼續活動,則會啟動指定的應用程式(如果尚未執行),並使用 中的承載 NSUserActivity 來重新啟動活動。

持續用戶活動的概觀

只有共用相同開發人員小組標識碼並回應指定 活動類型的 應用程式才有資格接續。 應用程式會定義它在 Info.plist 檔案索引鍵下NSUserActivityTypes支持的活動類型。 鑒於此,持續裝置會根據小組標識碼、活動類型,以及選擇性地 選擇活動標題,選擇應用程式執行接續。

接收應用程式會使用 字典中的資訊NSUserActivityUserInfo來設定其使用者介面,並還原指定活動的狀態,讓轉換看起來順暢地提供給使用者。

如果接續需要比透過 有效率地 NSUserActivity傳送更多的資訊,則繼續應用程式可以將呼叫傳送至原始應用程式,並建立一或多個數據流來傳輸所需的數據。 例如,如果活動正在編輯含有多個影像的大型文字檔,則需要串流,才能傳輸接收裝置上繼續活動所需的資訊。 如需詳細資訊,請參閱 下面的<支援接續數據流 >一節。

如上所述, NSDocumentUIDocument 以為基礎的應用程式會自動內建 Handoff 支援。 如需詳細資訊,請參閱 下方檔型應用程式中 的支援交接一節。

NSUserActivity 類別

類別 NSUserActivity 是 Handoff 交換中的主要物件,可用來封裝可供接續的用戶活動狀態。 應用程式會針對它支援的任何活動具現化 的複本 NSUserActivity ,並希望在另一個裝置上繼續。 例如,文件編輯器會為每個目前開啟的檔建立活動。 不過,只有最前面的檔(顯示在最前面視窗或索引標籤中)是 目前活動 ,並可供接續使用。

NSUserActivity 實例是由 其 ActivityTypeTitle 屬性所識別。 UserInfo字典屬性是用來攜帶活動狀態的相關信息。 NeedsSave如果您要延遲透過 NSUserActivity的委派載入狀態資訊,請將 屬性true設定為 。 AddUserInfoEntries使用方法,將其他用戶端的新數據合併到UserInfo字典中,以保留活動的狀態。

NSUserActivityDelegate 類別

NSUserActivityDelegate用來讓資訊保持在NSUserActivity最新狀態的UserInfo字典中,並與活動的目前狀態同步。 當系統需要更新活動中的資訊時(例如在另一部裝置上接續之前),它會呼叫 UserActivityWillSave 委派的方法。

您必須實作 方法,UserActivityWillSave並且對 (例如UserInfoTitle等等) 進行任何變更NSUserActivity,以確保它仍會反映目前活動的狀態。 當系統呼叫 UserActivityWillSave 方法時, NeedsSave 將會清除旗標。 如果您修改活動的任何資料屬性,則必須再次設定 NeedsSavetrue

您可以選擇性地自動擁有UIKitAppKit管理用戶活動,而不是使用UserActivityWillSave上述方法。 若要這樣做,請設定回應程式對象的 UserActivity 屬性並實作 UpdateUserActivityState 方法。 如需詳細資訊,請參閱下方回應程式中的支援交接一節。

App Framework 支援

UIKit (iOS) 和 AppKit (OS X) 都提供 、回應程式 (NSResponder/UIResponder) 和 AppDelegate 類別中 [交接] 的NSDocument內建支援。 雖然每個OS會以稍微不同的方式實作Handoff,但基本機制和API相同。

檔案型應用程式中的用戶活動

檔型 iOS 和 OS X 應用程式會自動內建 Handoff 支援。 若要啟用此支援,您必須為CFBundleDocumentTypes應用程式 Info.plist 檔案中的每個專案新增NSUbiquitousDocumentUserActivityType索引鍵和值。

如果此機碼存在, NSDocumentUIDocument 會自動建立 NSUserActivity 指定之類型之 iCloud 型文件的實例。 您必須為應用程式支援的每個檔案類型提供活動類型,以及多個檔案類型可以使用相同的活動類型。 和都會NSDocumentUIDocument使用 其 FileURL 屬性的值自動填入 UserInfo 的屬性NSUserActivity

在OS X上 NSUserActivity ,當文件的視窗變成主視窗時,由 AppKit 和相關聯的回應者會自動成為目前活動。 在 iOS 上,針對 NSUserActivityUIKit管理的物件,您必須明確呼叫 BecomeCurrent 方法,或在應用程式來到前景時,在 上UIViewController設定檔的 UserActivity 屬性。

AppKit 會自動還原OS X上以這種方式建立的任何 UserActivity 屬性。如果 ContinueUserActivity 方法傳 false 回或未實作,就會發生這種情況。 在此情況下,檔會以 OpenDocumentNSDocumentController 方法開啟,然後 RestoreUserActivityState 會收到方法呼叫。

如需詳細資訊,請參閱下方檔型應用程式中的支援交接一節。

用戶活動和回應者

UIKit如果您將它設定為回應者物件的 UserActivity 屬性,和 AppKit 都可以自動管理用戶活動。 如果狀態已修改,您必須將回應者的 UserActivity 屬性設定NeedsSavetrue。 在提供回應者更新狀態的時間後,系統會在需要時自動儲存 UserActivity ,方法是呼叫其 UpdateUserActivityState 方法。

如果多個回應者共用單 NSUserActivity 一實例,當系統更新用戶活動物件時,他們會收到回 UpdateUserActivityState 呼。 回應者必須呼叫 AddUserInfoEntries 方法來更新 NSUserActivityUserInfo 字典,以反映目前的活動狀態。 字典 UserInfo 會在每次 UpdateUserActivityState 呼叫之前清除。

若要將本身與活動解除關聯,回應者可以將其 UserActivity 屬性設定為 null。 當應用程式架構受控 NSUserActivity 實例沒有更多相關聯的回應者或檔時,它會自動失效。

如需詳細資訊,請參閱下方回應程式中的支援交接一節。

用戶活動和 AppDelegate

AppDelegate您的應用程式是處理 Handoff 接續時的主要進入點。 當使用者回應 Handoff 通知時,會啟動適當的應用程式(如果尚未執行),並 WillContinueUserActivityWithType 呼叫的 AppDelegate 方法。 此時,應用程式應該通知使用者接續正在啟動。

呼叫 NSUserActivityContinueUserActivity 方法時AppDelegate,會傳遞 實例。 此時,您應該設定應用程式的使用者介面,並繼續指定的活動。

如需詳細資訊,請參閱下方的 實作交接 一節。

在 Xamarin 應用程式中啟用交接

由於 Handoff 所強加的安全性需求,使用 Handoff 架構的 Xamarin.iOS 應用程式必須在 Apple 開發人員入口網站和 Xamarin.iOS 專案檔中正確設定。

執行下列操作:

  1. 登入Apple開發人員入口網站

  2. 按兩下 [ 憑證]、[標識符和配置檔]。

  3. 如果您尚未這麼做,請按兩下 [標識符] ,然後為您的應用程式建立標識碼(例如 com.company.appname),否則請編輯現有的標識碼。

  4. 確定 已檢查 iCloud 服務是否有指定的識別碼:

    為指定的標識碼啟用 iCloud 服務

  5. 儲存您的變更。

  6. 點選單擊 [布建設定檔>開發],並為您應用程式建立新的開發佈建配置檔:

    為應用程式建立新的開發佈建配置檔

  7. 下載並安裝新的布建配置檔,或使用 Xcode 下載並安裝設定檔。

  8. 編輯您的 Xamarin.iOS 選項,並確定您使用剛才建立的布建設定檔:

    選取剛建立的布建配置檔

  9. 接下來,編輯 Info.plist 檔案,並確定您使用用來建立布建配置檔的應用程式識別碼:

    設定應用程式識別碼

  10. 捲動至 [ 背景模式 ] 區段,並檢查下列專案:

    啟用必要的背景模式

  11. 儲存所有檔案的變更。

備妥這些設定後,應用程式現在已準備好存取 Handoff Framework API。 如需布建的詳細資訊,請參閱我們的 裝置布 建和 布建您的應用程式 指南。

實作交接

用戶活動可以在使用相同開發人員小組標識碼簽署的應用程式之間繼續,並支援相同的活動類型。 在 Xamarin.iOS 應用程式中實作 Handoff 需要您建立使用者活動物件(在 或 AppKitUIKit),更新物件的狀態以追蹤活動,並繼續接收裝置上的活動。

識別用戶活動

實作 Handoff 的第一個步驟是識別應用程式支援的用戶活動類型,並查看哪些活動是其他裝置上接續的好候選專案。 例如:ToDo 應用程式可能支援將專案編輯為一個使用者活動類型,並支援將可用的專案清單瀏覽為另一個。

應用程式可以視需要建立任意數量的用戶活動類型,一個用於應用程式提供的任何函式。 針對每個用戶活動類型,應用程式必須追蹤類型活動開始和結束的時間,而且需要維護最新的狀態資訊,才能在另一部裝置上繼續該工作。

用戶活動可以在任何以相同小組標識符簽署的應用程式上繼續執行,而不需要傳送和接收應用程式之間的任何一對一對應。 例如,指定的應用程式可以建立四種不同類型的活動,這些活動是由另一個裝置上的不同個別應用程式取用。 這是 Mac 版應用程式(可能有許多功能與功能)和 iOS 應用程式之間常見的情況,其中每個應用程式較小且著重於特定工作。

建立活動類型標識碼

活動類型標識碼是新增至NSUserActivityTypes應用程式 Info.plist 檔案陣列的簡短字串,用來唯一識別指定的用戶活動類型。 應用程式支援的每個活動都會有一個專案在陣列中。 Apple 建議使用活動類型標識碼的反向 DNS 樣式表示法,以避免衝突。 例如: com.company-name.appname.activity 針對特定應用程式型活動,或 com.company-name.activity 可跨多個應用程式執行的活動。

建立 NSUserActivity 實例以識別活動類型時,會使用活動類型識別碼。 當活動在另一個裝置上繼續時,活動類型(以及應用程式的小組標識符)會決定要啟動哪些應用程式以繼續活動。

例如,我們將建立名為 MonkeyBrowser 的範例應用程式。 此應用程式會顯示四個索引標籤,每個索引標籤都會在網頁瀏覽器檢視中開啟不同的 URL。 用戶將能夠繼續執行應用程式的不同 iOS 裝置上的任何索引標籤。

若要建立必要的活動類型標識碼以支援此行為,請編輯 Info.plist 檔案並切換至 [來源 ] 檢視。 NSUserActivityTypes新增金鑰並建立下列識別碼:

plist 編輯器中的 NSUserActivityTypes 索引鍵和必要標識碼

我們建立了四個新的活動類型標識碼,其中一個用於範例 MonkeyBrowser 應用程式中的每個索引標籤。 建立您自己的應用程式時,請將數位的內容 NSUserActivityTypes 取代為應用程式所支援活動特有的活動類型識別碼。

追蹤用戶活動變更

當我們建立 類別的新實例 NSUserActivity 時,我們會指定 NSUserActivityDelegate 實例來追蹤活動狀態的變更。 例如,下列程式代碼可用來追蹤狀態變更:

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

UserActivityReceivedData 接續數據流從傳送裝置接收數據時,會呼叫 方法。 如需詳細資訊,請參閱 下面的<支援接續數據流 >一節。

UserActivityWasContinued當另一個裝置接管目前裝置的活動時,會呼叫 方法。 視活動類型而定,例如將新專案新增至 ToDo 清單,應用程式可能需要中止傳送裝置上的活動。

系統會 UserActivityWillSave 先呼叫 方法,再儲存活動的任何變更,並跨本機可用的裝置進行同步處理。 您可以使用這個方法,在傳送實例之前,對 實例的 NSUserActivity 屬性進行任何最後一分鐘的變更UserInfo

建立 NSUserActivity 實例

您的應用程式想要提供在另一個裝置上繼續的可能性的每個活動都必須封裝在 實例中 NSUserActivity 。 應用程式可以視需要建立盡可能多的活動,而這些活動的性質取決於相關應用程式的功能和功能。 例如,電子郵件應用程式可能會建立一個活動來建立新訊息,另一個用於讀取郵件。

在我們的範例應用程式中, NSUserActivity 每次使用者在其中一個索引標籤網頁瀏覽器檢視中輸入新的 URL 時,都會建立新的 。 下列程式代碼會儲存指定索引標籤的狀態:

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

它會使用上面建立的其中一個用戶活動類型來建立新的 NSUserActivity ,併為活動提供人類可讀取的標題。 它會附加至上述建立的 NSUserActivityDelegate 實例,以監看狀態變更,並通知 iOS 此用戶活動是目前活動。

填入 UserInfo 字典

如上所述,類別 UserInfoNSUserActivity 屬性是 NSDictionary 用來定義指定活動狀態的索引鍵/值組。 儲存在 中的UserInfo值必須是下列其中一種類型:NSArray、、NSNullNSDateNSDictionaryNSNumberNSDataNSSetNSString或 。NSURL NSURL 指向 iCloud 檔案的數據值將會自動調整,使其指向接收裝置上的相同檔。

在上述範例中,我們已建立 物件,並填入單一 NSMutableDictionary 索引鍵,並提供使用者目前在指定索引標籤上檢視的 URL。用戶 AddUserInfoEntries 活動的方法用來更新活動,其中包含將用來還原接收裝置上活動的數據:

// 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 建議將資訊保持在最基本限,以確保活動會及時傳送到接收裝置。 如果需要較大的資訊,例如需要編輯附加至檔的影像,您應該使用接續串流。 如需詳細資訊,請參閱下方的一節。

繼續活動

交接會自動通知本機 iOS 和 OS X 裝置,這些裝置與原始裝置的實際鄰近性,並登入相同的 iCloud 帳戶,以取得持續性用戶活動的可用性。 如果使用者選擇在新裝置上繼續活動,系統將會啟動適當的應用程式(根據小組標識符和活動類型),以及其 AppDelegate 接續需要發生的資訊。

首先, WillContinueUserActivityWithType 呼叫 方法,讓應用程式可以通知使用者接續即將開始。 我們在範例應用程式的 AppDelegate.cs 檔案中使用下列程式代碼來處理接續:

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

在上述範例中,每個檢視控制器都會向 AppDelegate 註冊,並具有一個公用 PreparingToHandoff 方法來顯示活動指標,並顯示一則訊息,讓使用者知道活動即將交給目前的裝置。 範例:

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

ContinueUserActivity將呼叫 的 AppDelegate ,以實際繼續指定的活動。 同樣地,從我們的範例應用程式:

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

每個檢視控制器的公用 PerformHandoff 方法實際上會預先設定交接,並還原目前裝置上的活動。 在範例的案例中,它會在指定的索引標籤中顯示使用者在不同裝置上流覽的相同 URL。 範例:

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

}

方法 ContinueUserActivity 包含 UIApplicationRestorationHandler 的 ,您可以呼叫檔或回應程式型活動繼續。 呼叫 時,您必須將 或 可還原的對象傳遞 NSArray 至還原處理程式。 例如:

completionHandler (new NSObject[]{Tab4});

針對傳遞的每個物件,將會呼叫其 RestoreUserActivityState 方法。 然後,每個物件都可以使用字典中的數據 UserInfo 來還原自己的狀態。 例如:

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

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

對於檔案型應用程式,如果您未實 ContinueUserActivity 作 方法或傳回 falseUIKitAppKit 可以自動繼續活動。 如需詳細資訊,請參閱下方檔型應用程式中的支援交接一節。

失敗交接正常

由於 Handoff 依賴集合之間鬆散連線的 iOS 和 OS X 裝置之間的資訊傳輸,因此傳輸程式有時會失敗。 您應該設計應用程式以正常處理這些失敗,並通知用戶發生的任何情況。

發生失敗時, DidFailToContinueUserActivitiy 將會呼叫的 AppDelegate 方法。 例如:

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

您應該使用 提供的 NSError ,向使用者提供有關失敗的資訊。

原生應用程式到網頁瀏覽器交接

使用者可能會想要繼續活動,而不需要在所需的裝置上安裝適當的原生應用程式。 在某些情況下,Web 介面可能會提供所需的功能,而且活動仍可繼續。 例如,使用者的電子郵件帳戶可能會提供 Web 基底 UI 來撰寫和讀取訊息。

如果原始原生應用程式知道 Web 介面的 URL(以及識別所要繼續指定專案的必要語法),則可以在 實例的 NSUserActivity 屬性中WebpageURL編碼這項資訊。 如果接收裝置未安裝適當的原生應用程式來處理接續,則可以呼叫提供的 Web 介面。

網頁瀏覽器至原生應用程式交接

如果使用者在原始裝置上使用 Web 型介面,而接收裝置上的原生應用程式會宣告 屬性的 WebpageURL 網域部分,則系統會使用該應用程式來處理接續。 新的裝置會收到將 NSUserActivity 活動類型標示為 BrowsingWeb 的實例,且 WebpageURL 將包含使用者正在流覽的URL,字典將會是空的 UserInfo

若要讓應用程式參與這種類型的 Handoff,它必須以具有格式<service>:<fully qualified domain name>的權利宣告網域com.apple.developer.associated-domains(例如:)。 activity continuation:company.com

如果指定的網域符合 WebpageURL 屬性的值,Handoff 會從該網域的網站下載已核准的應用程式標識符清單。 網站必須在名為 apple-app-site-association 的已簽署 JSON 檔案中提供已核准識別符的清單(例如, https://company.com/apple-app-site-association

此 JSON 檔案包含字典,指定表單 <team identifier>.<bundle identifier>中的應用程式識別碼清單。 例如:

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

若要簽署 JSON 檔案(使其具有正確的 Content-Typeapplication/pkcs7-mime),請使用終端機應用程式和openssl命令搭配 iOS 信任的證書頒發機構單位所簽發的憑證和金鑰(請參閱以取得清單)。https://support.apple.com/kb/ht5012 例如:

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

命令openssl會輸出您在網站上放置於 apple-app-site-association URL 的已簽署 JSON 檔案。 例如:

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

應用程式將會收到其 WebpageURL 網域在其權利中 com.apple.developer.associated-domains 的任何活動。 http僅支援 和 https 通訊協定,任何其他通訊協定都會引發例外狀況。

支援檔案型應用程式中的交接

如上所述,在iOS和OS X上,如果應用程式的 Info.plist 檔案包含 CFBundleDocumentTypes 的索引鍵 NSUbiquitousDocumentUserActivityType,檔型應用程式將會自動支援iCloud型檔的交接。 例如:

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

在此範例中,字串是附加活動名稱的反向 DNS 應用程式指示項。 如果以此方式輸入,則不需要在 Info.plist 檔案的陣列中NSUserActivityTypes重複活動類型專案。

自動建立的用戶活動物件(可透過文件 UserActivity 的屬性取得)可由應用程式中的其他對象參考,並用來還原接續狀態。 例如,若要追蹤項目選取範圍和檔位置。 每當狀態變更並更新 方法中的UpdateUserActivityState字典時,您必須將此活動NeedsSave屬性設定為 trueUserInfo

UserActivity屬性可從任何線程使用,並符合索引鍵/值觀察 (KVO) 通訊協定,因此可用來在文件移入和移出 iCloud 時保持同步。 關閉檔時,屬性 UserActivity 將會失效。

如需詳細資訊,請參閱以檔為基礎的應用程式檔中的Apple用戶活動支援。

支持回應者中的交接

您可以藉由設定UserActivity回應者的屬性,將回應者(繼承自 UIResponder iOS 或 NSResponder OS X 上)與活動產生關聯。 系統會在適當的時間自動儲存 UserActivity 屬性,呼叫響應程式 UpdateUserActivityState 的方法,以使用 AddUserInfoEntriesFromDictionary 方法將目前的數據新增至 User Activity 物件。

支援接續數據流

可能是初始 Handoff 承載無法有效率地傳輸繼續活動所需的資訊量的情況。 在這些情況下,接收應用程式可以在本身與原始應用程式之間建立一或多個數據流,以傳輸數據。

原始應用程式會將 實體的 NSUserActivity 屬性設定SupportsContinuationStreamstrue。 例如:

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

接收應用程式接著可以呼叫 GetContinuationStreamsNSUserActivityAppDelegate 的方法,以建立數據流。 例如:

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

在原始裝置上,用戶活動委派會藉由呼叫其 DidReceiveInputStream 方法來接收數據流,以提供要求繼續裝置上用戶活動的數據。

您將使用 NSInputStream 提供數據流數據的唯讀取權,並提供 NSOutputStream 唯寫存取權。 數據流應該以要求和回應方式使用,其中接收應用程式會要求更多數據,而原始應用程式會提供它。 因此,寫入原始裝置上輸出數據流的數據會從繼續裝置上的輸入數據流讀取,反之亦然。

即使在需要接續串流的情況下,兩個應用程式之間的來回通訊應該最少。

如需詳細資訊,請參閱 Apple 的 Using Continuation Streams 檔。

交接最佳做法

透過 Handoff 順利實作「用戶活動」的無縫接續,需要仔細設計,因為涉及的所有元件。 Apple 建議針對已啟用 Handoff 的應用程式採用下列最佳做法:

  • 設計您的用戶活動,以要求盡可能最小的承載,讓活動的狀態保持繼續。 承載愈大,接續需要較長的時間才能啟動。
  • 如果您必須傳輸大量數據以成功接續,請考慮到設定和網路額外負荷所涉及的成本。
  • 大型 Mac 應用程式通常會建立由 iOS 裝置上數個、較小、工作特定應用程式所處理的用戶活動。 不同的應用程式和 OS 版本應該設計成搭配運作良好或正常失敗。
  • 指定活動類型時,請使用反向 DNS 表示法來避免衝突。 如果活動是指定應用程式特有的,則其名稱應該包含在類型定義中(例如 com.myCompany.myEditor.editing)。 如果活動可以跨多個應用程式運作,請從定義中卸除應用程式名稱(例如 com.myCompany.editing)。
  • 如果應用程式需要更新使用者活動的狀態(NSUserActivity) 將 NeedsSave 屬性設定為 true。 適當時候,Handoff 會呼叫委派的 UserActivityWillSave 方法,以便視需要更新 UserInfo 字典。
  • 由於接收裝置上的 Handoff 程式可能不會立即初始化,因此您應該實 AppDelegate作 的 WillContinueUserActivity ,並通知使用者接續即將啟動。

範例交接應用程式

在 Xamarin.iOS 應用程式中使用 Handoff 的範例是 MonkeyBrowser 範例應用程式。 應用程式有四個索引標籤可供使用者用來流覽 Web,每個索引標籤都有指定的活動類型:天氣、我的最愛、咖啡休息和工作。

在任何索引標籤上,當使用者輸入新的 URL 並點 選 [Go ] 按鈕時,就會針對該索引標籤建立新的 NSUserActivity ,其中包含使用者目前流覽的 URL:

範例交接應用程式

如果另一個使用者的裝置 已安裝 MonkeyBrowser 應用程式,則會使用相同的使用者帳戶登入 iCloud、位於相同的網路上,且靠近上述裝置,則 [交接活動] 會顯示在主畫面上(左下角):

左下角主畫面上顯示的 [交接活動]

如果使用者在 [交接] 圖示上向上拖曳,將會啟動應用程式,並在新的裝置上繼續指定的 NSUserActivity 用戶活動:

新裝置上的用戶活動繼續

成功將用戶活動傳送至另一個 Apple 裝置時,傳送裝置將會在其上NSUserActivityDelegate收到方法的NSUserActivity呼叫UserActivityWasContinued,讓其知道用戶活動已成功傳輸到另一部裝置。

摘要

本文已介紹 Handoff 架構,用來繼續使用者多個 Apple 裝置之間的用戶活動。 接下來,它示範如何在 Xamarin.iOS 應用程式中啟用和實作 Handoff。 最後,它討論了可用的不同類型的 Handoff 接續,以及 Handoff 最佳做法。