다음을 통해 공유


Xamarin.iOS에서 핸드오프

이 문서에서는 Xamarin.iOS 앱에서 Handoff를 사용하여 사용자의 다른 디바이스에서 실행되는 앱 간에 사용자 활동을 전송하는 방법을 설명합니다.

Apple은 iOS 8 및 OS X Yosemite(10.10)에서 핸드오프를 도입하여 사용자가 디바이스 중 하나에서 시작된 활동을 동일한 앱 또는 동일한 활동을 지원하는 다른 앱을 실행하는 다른 디바이스로 전송하는 일반적인 메커니즘을 제공했습니다.

핸드오프 작업을 수행하는 예제

이 문서에서는 Xamarin.iOS 앱에서 활동 공유를 사용하도록 설정하는 것을 간단히 살펴보고 핸드오프 프레임워크에 대해 자세히 설명합니다.

핸드오프 정보

핸드오프(연속성이라고도 함)는 사용자가 iOS 8 및 OS X Yosemite(10.10)에서 사용자가 디바이스 중 하나(iOS 또는 Mac)에서 작업을 시작하고 다른 장치에서 동일한 작업을 계속하는 방법으로 도입되었습니다(사용자의 iCloud 계정으로 식별됨).

핸드오프는 새로운 향상된 검색 기능도 지원하도록 iOS 9에서 확장되었습니다. 자세한 내용은 향상된 검색 설명서를 참조하세요.

예를 들어 사용자는 i전화 전자 메일을 시작하고 동일한 메시지 정보를 모두 입력하고 커서를 iOS에 남겨둔 동일한 위치에 있는 Mac에서 전자 메일을 원활하게 계속할 수 있습니다.

동일한 팀 ID 를 공유하는 앱은 iTunes 앱 스토어를 통해 배달되거나 등록된 개발자(Mac, Enterprise 또는 임시 앱용)가 서명한 경우 핸드오프를 사용하여 앱 간에 사용자 활동을 계속할 수 있습니다.

모든 NSDocument 또는 UIDocument 기반 앱은 자동으로 핸드오프 지원을 기본 제공하며, Handoff를 지원하기 위해 최소한의 변경이 필요합니다.

지속적인 사용자 활동

클래스( NSUserActivity 일부 변경 UIKit 내용 및 AppKit)는 잠재적으로 다른 사용자의 디바이스에서 계속될 수 있는 사용자의 활동을 정의하기 위한 지원을 제공합니다.

활동을 다른 사용자의 디바이스로 전달하려면 현재 활동으로 표시된 인스턴스NSUserActivity에 캡슐화되어야 하며 페이로드 집합(연속을 수행하는 데 사용되는 데이터)이 있고 해당 디바이스로 활동을 전송해야 합니다.

핸드오프는 iCloud를 통해 더 큰 데이터 패킷이 동기화되는 상태에서 계속할 활동을 정의하기 위해 최소한의 정보를 전달합니다.

수신 디바이스에서 사용자는 활동을 계속 사용할 수 있다는 알림을 받게 됩니다. 사용자가 새 디바이스에서 작업을 계속하도록 선택하면 지정된 앱이 시작되고(아직 실행되지 않은 경우) 해당 앱의 NSUserActivity 페이로드가 활동을 다시 시작하는 데 사용됩니다.

지속적인 사용자 활동 개요

동일한 개발자 팀 ID를 공유하고 지정된 활동 유형 에 응답하는 앱만 연속할 수 있습니다. 앱은 Info.plist 파일의 키 아래에서 NSUserActivityTypes 지원하는 활동 유형을 정의합니다. 이 경우 연속 디바이스는 팀 ID, 활동 유형 및 선택적으로 활동 제목에 따라 연속 작업을 수행할 앱을 선택합니다.

수신 앱은 사전의 UserInfo 정보를 NSUserActivity사용하여 사용자 인터페이스를 구성하고 지정된 활동의 상태를 복원하여 전환이 최종 사용자에게 원활하게 표시되도록 합니다.

연속 작업을 통해 NSUserActivity효율적으로 전송할 수 있는 것보다 더 많은 정보가 필요한 경우 다시 시작 앱은 원래 앱에 호출을 보내고 필요한 데이터를 전송하기 위해 하나 이상의 스트림을 설정할 수 있습니다. 예를 들어 작업이 여러 이미지로 큰 텍스트 문서를 편집하는 경우 수신 디바이스에서 작업을 계속하는 데 필요한 정보를 전송하려면 스트리밍이 필요합니다. 자세한 내용은 아래의 연속 스트림 지원 섹션을 참조하세요.

위에서 설명한 대로 또는 NSDocumentUIDocument 기반 앱에는 자동으로 Handoff 지원이 기본 제공됩니다. 자세한 내용은 아래 문서 기반 앱지원 핸드오프 섹션을 참조하세요.

NSUserActivity 클래스

NSUserActivity 클래스는 핸드오프 교환의 기본 개체이며 연속 작업에 사용할 수 있는 사용자 작업의 상태를 캡슐화하는 데 사용됩니다. 앱은 지원하는 모든 활동에 대한 복사본 NSUserActivity 을 인스턴스화하고 다른 디바이스에서 계속하려고 합니다. 예를 들어 문서 편집기에서는 현재 열려 있는 각 문서에 대한 작업을 만듭니다. 그러나 맨 앞의 문서(맨 앞 창 또는 탭에 표시됨)만 현재 작업이므로 계속 작업을 수행할 수 있습니다.

인스턴스 NSUserActivity 는 해당 ActivityType 인스턴스와 Title 속성 둘 다로 식별됩니다. UserInfo 사전 속성은 활동의 상태에 대한 정보를 전달하는 데 사용됩니다. NeedsSave 속성을 '의 대리자를 true 통해 NSUserActivity상태 정보를 지연 로드하려는 경우로 설정합니다. AddUserInfoEntries 활동의 상태를 유지하기 위해 필요에 따라 다른 클라이언트 UserInfo 의 새 데이터를 사전에 병합하려면 이 메서드를 사용합니다.

NSUserActivityDelegate 클래스

NSUserActivityDelegate 기능은 정보를 NSUserActivity'의 UserInfo 사전을 최신 상태로 유지하고 활동의 현재 상태와 동기화하는 데 사용됩니다. 시스템에서 활동의 정보를 업데이트해야 하는 경우(예: 다른 디바이스에서 계속하기 전) 대리자의 메서드를 UserActivityWillSave 호출합니다.

메서드를 UserActivityWillSave 구현하고 현재 작업의 상태를 계속 반영하도록 (예: UserInfoTitle등)를 변경 NSUserActivity 해야 합니다. 시스템에서 메서드를 호출하면 UserActivityWillSave 플래그가 NeedsSave 지워질 것입니다. 활동의 데이터 속성을 수정하는 경우 다시 설정 NeedsSavetrue 해야 합니다.

위에 제공된 메서드를 UserActivityWillSave 사용하는 대신 필요에 따라 사용자 활동을 자동으로 사용 UIKit 하거나 AppKit 관리할 수 있습니다. 이렇게 하려면 응답자 개체의 UserActivity 속성을 설정하고 메서드를 구현합니다 UpdateUserActivityState . 자세한 내용은 아래 응답자의 지원 핸드오프 섹션을 참조하세요.

App Framework 지원

(iOS) 및 (OS X) 모두 UIKit , 응답기(NSResponderUIResponder/) 및 AppDelegate 클래스의 핸드오프에 NSDocument대한 기본 제공 지원을 제공합니다.AppKit 각 OS는 핸드오프를 약간 다르게 구현하지만 기본 메커니즘과 API는 동일합니다.

문서 기반 앱의 사용자 활동

문서 기반 iOS 및 OS X 앱에는 자동으로 핸드오프 지원이 기본 제공됩니다. 이 지원을 활성화하려면 앱의 Info.plist 파일에서 각 CFBundleDocumentTypes 항목에 대한 키와 값을 추가 NSUbiquitousDocumentUserActivityType 해야 합니다.

이 키가 있는 경우 지정된 형식의 iCloud 기반 문서에 대한 인스턴스를 자동으로 NSDocumentUIDocument 만듭니 NSUserActivity 다. 앱에서 지원하는 각 문서 유형에 대해 작업 유형을 제공해야 하며 여러 문서 형식이 동일한 작업 형식을 사용할 수 있습니다. 둘 다 속성의 속성을 해당 FileURL 속성 값으로 자동으로 채웁다UserInfo.NSUserActivityUIDocumentNSDocument

OS X에서 NSUserActivity 문서의 창이 기본 창이 되면 응답자에 의해 AppKit 관리되고 응답자와 연결된 사용자가 자동으로 현재 활동이 됩니다. iOS에서 관리되는 UIKit개체의 경우 NSUserActivity 명시적으로 메서드를 호출 BecomeCurrent 하거나 앱이 UserActivity 포그라운드에 UIViewController 올 때 문서의 속성을 설정해야 합니다.

AppKit 는 OS X에서 이러한 방식으로 생성된 모든 UserActivity 속성을 자동으로 복원합니다. 이 문제는 메서드가 ContinueUserActivity 반환 false 되거나 구현되지 않은 경우에 발생합니다. 이 경우 문서가 메서드 NSDocumentController 를 사용하여 OpenDocument 열리고 메서드 호출이 RestoreUserActivityState 수신됩니다.

자세한 내용은 아래 문서 기반 앱의 지원 핸드오프 섹션을 참조하세요.

사용자 활동 및 응답자

AppKit 둘 다 UIKit 응답기 개체의 UserActivity 속성으로 설정하는 경우 사용자 활동을 자동으로 관리할 수 있습니다. 상태가 수정된 경우 응답자 UserActivitytrue의 속성을 .로 설정 NeedsSave 해야 합니다. 응답기에서 해당 메서드를 UserActivity 호출 UpdateUserActivityState 하여 상태를 업데이트할 시간을 준 후 필요한 경우 시스템이 자동으로 저장됩니다.

여러 응답자가 단일 NSUserActivity 인스턴스를 공유하는 경우 시스템에서 사용자 활동 개체를 업데이트할 때 콜백을 받 UpdateUserActivityState 습니다. 응답자는 이 시점에서 현재 활동 상태를 반영하도록 '의 UserInfo 사전을 업데이트NSUserActivity하는 메서드를 호출 AddUserInfoEntries 해야 합니다. 사전은 UserInfoUpdateUserActivityState 호출 전에 지워집니다.

활동에서 자신을 연결 해제하기 위해 응답자는 해당 UserActivity 속성을 null.로 설정할 수 있습니다. 앱 프레임워크 관리 NSUserActivity 형 인스턴스에 연결된 응답자 또는 문서가 더 이상 없으면 자동으로 무효화됩니다.

자세한 내용은 아래 응답자의 지원 핸드오프 섹션을 참조하세요.

사용자 활동 및 AppDelegate

핸드오프 연속 작업을 처리할 때 앱은 AppDelegate 기본 진입점입니다. 사용자가 핸드오프 알림에 응답하면 적절한 앱이 시작되고 WillContinueUserActivityWithType (아직 실행되지 않은 경우) 메서드가 AppDelegate 호출됩니다. 이 시점에서 앱은 연속 작업이 시작되고 있음을 사용자에게 알려야 합니다.

인스턴스는 NSUserActivity 's ContinueUserActivity 메서드가 AppDelegate호출될 때 전달됩니다. 이 시점에서 앱의 사용자 인터페이스를 구성하고 지정된 활동을 계속해야 합니다.

자세한 내용은 아래의 핸드오프 구현 섹션을 참조하세요.

Xamarin 앱에서 핸드오프 사용

Handoff에서 적용한 보안 요구 사항으로 인해 핸드오프 프레임워크를 사용하는 Xamarin.iOS 앱은 Apple 개발자 포털과 Xamarin.iOS 프로젝트 파일 모두에서 올바르게 구성되어야 합니다.

다음을 수행하십시오:

  1. Apple 개발자 포털에 로그인합니다.

  2. 인증서, 식별자 및 프로필을 클릭합니다.

  3. 아직 수행하지 않은 경우 식별자를 클릭하고 앱의 ID를 만듭니다(예: com.company.appname). 그렇지 않으면 기존 ID를 편집합니다.

  4. iCloud 서비스가 지정된 ID에 대해 검사 있는지 확인합니다.

    지정된 ID에 대해 iCloud 서비스를 사용하도록 설정

  5. 변경 내용을 저장합니다.

  6. 프로비저닝 프로필>개발을 클릭하고 앱에 대한 새 개발 프로비저닝 프로필을 만듭니다.

    앱에 대한 새 개발 프로비저닝 프로필 만들기

  7. 새 프로비저닝 프로필을 다운로드하여 설치하거나 Xcode를 사용하여 프로필을 다운로드하고 설치합니다.

  8. Xamarin.iOS 프로젝트 옵션을 편집하고 방금 만든 프로비저닝 프로필을 사용하고 있는지 확인합니다.

    방금 만든 프로비저닝 프로필 선택

  9. 다음으로 Info.plist 파일을 편집하고 프로비저닝 프로필을 만드는 데 사용된 앱 ID를 사용하고 있는지 확인합니다.

    앱 ID 설정

  10. 백그라운드 모드 섹션으로 스크롤하여 다음 항목을 검사.

    필요한 백그라운드 모드 사용

  11. 모든 파일에 변경 내용을 저장합니다.

이러한 설정이 준비되면 애플리케이션이 이제 Handoff Framework API에 액세스할 준비가 되었습니다. 프로비저닝에 대한 자세한 내용은 Device Provisioning and Provisioning Your App 가이드를 참조하세요.

핸드오프 구현

동일한 개발자 팀 ID로 서명되고 동일한 활동 유형을 지원하는 앱 간에 사용자 활동을 계속할 수 있습니다. Xamarin.iOS 앱에서 핸드오프를 구현하려면 사용자 활동 개체를 UIKit 만들거나 AppKit, 개체의 상태를 업데이트하여 활동을 추적하고, 수신 디바이스에서 작업을 계속해야 합니다.

사용자 활동 식별

핸드오프를 구현하는 첫 번째 단계는 앱이 지원하는 사용자 활동 유형을 식별하고 해당 활동 중 다른 디바이스에서 계속하기에 적합한 활동을 확인하는 것입니다. 예를 들어 ToDo 앱은 항목 편집을 하나의 사용자 활동 유형으로 지원하고 사용 가능한 항목 목록을 다른 항목 목록으로 검색하도록 지원할 수 있습니다.

앱은 앱이 제공하는 모든 함수에 대해 필요한 만큼의 사용자 활동 형식을 만들 수 있습니다. 각 사용자 활동 유형에 대해 앱은 형식의 활동이 시작되고 끝나는 시기를 추적해야 하며, 다른 디바이스에서 해당 작업을 계속하려면 기본 최신 상태 정보를 확인해야 합니다.

보내기 및 수신 앱 간에 일대일 매핑 없이 동일한 팀 ID로 서명된 모든 앱에서 사용자 활동을 계속할 수 있습니다. 예를 들어 지정된 앱은 다른 디바이스에서 서로 다른 개별 앱에서 사용하는 네 가지 유형의 활동을 만들 수 있습니다. 이는 Mac 버전의 앱(많은 기능과 함수가 있을 수 있는)과 iOS 앱 간에 일반적으로 발생하며, 각 앱은 더 작고 특정 작업에 초점을 맞췄습니다.

활동 유형 식별자 만들기

활동 유형 식별자는 지정된 사용자 활동 유형을 고유하게 식별하는 데 사용되는 앱의 Info.plist 파일 배열에 추가 NSUserActivityTypes 된 짧은 문자열입니다. 앱이 지원하는 각 활동에 대해 배열에 하나의 항목이 있습니다. Apple은 충돌을 방지하기 위해 활동 유형 식별자에 역방향 DNS 스타일 표기법을 사용할 것을 제안합니다. 예를 들어 com.company-name.appname.activity 특정 앱 기반 활동 또는 com.company-name.activity 여러 앱에서 실행할 수 있는 활동의 경우입니다.

활동 유형 식별자는 활동 유형을 식별하는 인스턴스를 NSUserActivity 만들 때 사용됩니다. 다른 디바이스에서 활동이 계속되면 활동 유형(앱의 팀 ID와 함께)은 활동을 계속하기 위해 시작할 앱을 결정합니다.

예를 들어 MonkeyBrowser라는 샘플 앱을 만들려고 합니다. 이 앱은 각각 웹 브라우저 보기에서 서로 다른 URL이 열려 있는 4개의 탭을 제공합니다. 사용자는 앱을 실행하는 다른 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 캡슐화되어야 합니다. 앱은 필요한 만큼의 활동을 만들 수 있으며 해당 활동의 특성은 해당 앱의 기능과 기능에 따라 달라집니다. 예를 들어 전자 메일 앱은 새 메시지를 만들기 위한 하나의 활동과 메시지를 읽는 작업을 만들 수 있습니다.

예제 앱의 경우 사용자가 탭 웹 브라우저 보기 중 하나에 새 URL을 입력할 때마다 새 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 사전 채우기

위에서 UserInfo 살본 것처럼 클래스의 NSUserActivity 속성은 NSDictionary 지정된 활동의 상태를 정의하는 데 사용되는 키-값 쌍입니다. 저장된 UserInfo 값은 다음 형식 NSDataNSStringNSArrayNSDictionaryNSURLNSDateNSNullNSNumberNSSet중 하나여야 합니다. 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은 활동이 수신 디바이스에 적시에 전송되도록 가장 바레스트로 전송된 정보를 유지하는 것이 좋습니다. 문서에 첨부된 이미지를 편집해야 하는 등 더 큰 정보가 필요한 경우 연속 스트림 사용해야 합니다. 자세한 내용은 아래의 연속 스트림 지원 섹션을 참조하세요.

활동 계속

핸드오프는 원래 디바이스에 물리적으로 근접하고 동일한 iCloud 계정에 로그인한 로컬 iOS 및 OS X 디바이스에 계속 가능한 사용자 활동의 가용성을 자동으로 알릴 것입니다. 사용자가 새 디바이스에서 활동을 계속하도록 선택하는 경우 시스템은 적절한 앱(팀 ID 및 활동 유형에 따라)을 시작하고 연속 작업이 발생해야 하는 정보를 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...");
}

ContinueUserActivityAppDelegate 지정된 활동을 실제로 계속하기 위해 호출됩니다. 다시 예제 앱에서 다음을 수행합니다.

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 구현하지 않거나 반환 falseUIKit 하거나 AppKit 활동을 자동으로 다시 시작할 수 있습니다. 자세한 내용은 아래 문서 기반 앱의 지원 핸드오프 섹션을 참조하세요.

정상적으로 핸드오프 실패

핸드오프는 느슨하게 연결된 iOS 및 OS X 디바이스 간의 정보 전송에 의존하므로 전송 프로세스가 실패할 수 있습니다. 이러한 오류를 정상적으로 처리하고 사용자에게 발생하는 상황을 알리도록 앱을 디자인해야 합니다.

오류가 발생할 경우 메서드가 DidFailToContinueUserActivitiyAppDelegate 호출됩니다. 예시:

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 정보를 사용하여 사용자에게 오류에 대한 정보를 제공해야 합니다.

네이티브 앱-웹 브라우저 핸드오프

사용자가 원하는 디바이스에 적절한 네이티브 앱을 설치하지 않고도 작업을 계속할 수 있습니다. 경우에 따라 웹 기반 인터페이스가 필요한 기능을 제공할 수 있으며 작업을 계속 수행할 수 있습니다. 예를 들어 사용자의 전자 메일 계정은 메시지 작성 및 읽기를 위한 웹 기반 UI를 제공할 수 있습니다.

원래 네이티브 앱은 웹 인터페이스의 URL(그리고 계속되는 지정된 항목을 식별하는 데 필요한 구문)을 알고 있는 경우 인스턴스의 NSUserActivity 속성에서 WebpageURL 이 정보를 인코딩할 수 있습니다. 수신 디바이스에 연속 작업을 처리하기 위해 적절한 네이티브 앱이 설치되어 있지 않은 경우 제공된 웹 인터페이스를 호출할 수 있습니다.

웹 브라우저에서 네이티브 앱으로 전달

사용자가 원래 디바이스에서 웹 기반 인터페이스를 사용하고 있고 수신 디바이스의 네이티브 앱이 속성의 WebpageURL do기본 부분을 주장하는 경우 시스템은 해당 앱을 사용하여 연속 작업을 처리합니다. 새 디바이스는 활동 유형을 나타내는 BrowsingWeb 인스턴스를 수신 NSUserActivity 하고 WebpageURL 사용자가 방문한 URL을 포함하며 사전은 UserInfo 비어 있습니다.

앱이 이러한 유형의 핸드오프에 참여하려면 형식(예: activity continuation:company.com)이 있는 권한 <service>:<fully qualified domain name> 으로 do기본 com.apple.developer.associated-domains 클레임해야 합니다.

지정한 do기본 속성 값과 일치하는 WebpageURL 경우 Handoff는 웹 사이트에서 승인된 앱 ID 목록을 다운로드합니다기본. 웹 사이트는 apple-app-site-association(예https://company.com/apple-app-site-association: )라는 서명된 JSON 파일에 승인된 ID 목록을 제공해야 합니다.

이 JSON 파일에는 양식 <team identifier>.<bundle identifier>의 앱 ID 목록을 지정하는 사전이 포함되어 있습니다. 예시:

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

올바른 Content-Typeapplication/pkcs7-mimeJSON 파일에 서명하려면 iOS에서 신뢰할 수 있는 인증 기관에서 발급한 인증서 및 키와 함께 터미널openssl 과 명령을 사용합니다(목록 참조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 명령은 웹 사이트에 배치하는 서명된 JSON 파일을 apple-app-site-association URL에 출력합니다. 예시:

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

앱은 해당 권한에 있는 com.apple.developer.associated-domains 기본 모든 활동을 WebpageURL 받게 됩니다. httphttps 프로토콜만 지원되며 다른 프로토콜은 예외를 발생합니다.

문서 기반 앱에서 핸드오프 지원

위에서 설명한 대로 iOS 및 OS X에서 문서 기반 앱은 앱의 Info.plist 파일에 키가 NSUbiquitousDocumentUserActivityType포함된 경우 iCloud 기반 문서의 핸드오프를 CFBundleDocumentTypes 자동으로 지원합니다. 예시:

<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 다른 개체에서 참조할 수 있으며 연속 시 상태를 복원하는 데 사용할 수 있습니다. 예를 들어 항목 선택 및 문서 위치를 추적합니다. 이 활동 NeedsSave 속성을 상태가 변경되고 메서드의 사전 UpdateUserActivityStateUserInfo 업데이트할 true 때마다 설정해야 합니다.

이 속성은 UserActivity 모든 스레드에서 사용할 수 있으며 KVO(키-값 관찰) 프로토콜을 준수하므로 iCloud 안팎으로 이동할 때 문서를 동기화 상태로 유지하는 데 사용할 수 있습니다. UserActivity 문서를 닫으면 속성이 무효화됩니다.

자세한 내용은 문서 기반 앱 설명서에서 Apple의 사용자 활동 지원을 참조하세요.

응답자의 핸드오프 지원

iOS 또는 NSResponder OS X에서 UIResponder 상속된 응답자를 해당 속성을 설정하여 활동에 연결할 수 있습니다UserActivity. 시스템은 적절한 시간에 속성을 자동으로 저장하여 UserActivity 응답자의 UpdateUserActivityState 메서드를 호출하여 메서드를 사용하여 AddUserInfoEntriesFromDictionary User Activity 개체에 현재 데이터를 추가합니다.

연속 스트림 지원

활동을 계속하는 데 필요한 정보의 양이 초기 핸드오프 페이로드에 의해 효율적으로 전송될 수 없는 상황일 수 있습니다. 이러한 상황에서 수신 앱은 자체와 원래 앱 간에 하나 이상의 스트림을 설정하여 데이터를 전송할 수 있습니다.

원래 앱은 인스턴스trueSupportsContinuationStreamsNSUserActivity 속성을 .로 설정합니다. 예시:

// 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의 연속 사용 스트림 설명서를 참조하세요.

핸드오프 모범 사례

Handoff를 통해 사용자 활동의 원활한 연속을 성공적으로 구현하려면 관련된 모든 다양한 구성 요소 때문에 신중한 디자인이 필요합니다. Apple은 핸드오프 사용 앱에 대해 다음과 같은 모범 사례를 채택할 것을 제안합니다.

  • 작업 상태를 계속하기 위해 가능한 가장 작은 페이로드가 필요하도록 사용자 활동을 디자인합니다. 페이로드가 클수록 연속 작업을 시작하는 데 시간이 오래 걸립니다.
  • 성공적인 연속 작업을 위해 많은 양의 데이터를 전송해야 하는 경우 구성 및 네트워크 오버헤드와 관련된 비용을 고려합니다.
  • 대용량 Mac 앱은 iOS 디바이스에서 몇 가지 작은 작업별 앱에서 처리하는 사용자 활동을 만드는 것이 일반적입니다. 서로 다른 앱 및 OS 버전은 함께 잘 작동하거나 정상적으로 실패하도록 설계되어야 합니다.
  • 작업 유형을 지정할 때 충돌을 방지하려면 역방향 DNS 표기법을 사용합니다. 활동이 지정된 앱과 관련된 경우 해당 이름은 형식 정의(예: com.myCompany.myEditor.editing)에 포함되어야 합니다. 작업이 여러 앱에서 작동할 수 있는 경우 정의에서 앱 이름을 삭제합니다(예: com.myCompany.editing).
  • 앱이 사용자 작업(NSUserActivity)의 상태를 업데이트해야 하는 경우 속성을 true.로 설정합니다NeedsSave. 적절한 시간에 Handoff는 필요에 따라 사전을 업데이트할 수 있도록 대리자의 UserActivityWillSave 메서드를 UserInfo 호출합니다.
  • 핸드오프 프로세스가 수신 디바이스에서 즉시 초기화되지 않을 수 있으므로 '를 WillContinueUserActivity 구현AppDelegate하고 연속 작업이 시작되도록 사용자에게 알려야 합니다.

핸드오프 앱 예제

Xamarin.iOS 앱에서 Handoff를 사용하는 예제는 MonkeyBrowser 샘플 앱입니다. 앱에는 사용자가 웹을 탐색하는 데 사용할 수 있는 4개의 탭이 있으며, 각각 지정된 활동 유형(날씨, 즐겨찾기, 커피 나누기 및 작업)이 있습니다.

모든 탭에서 사용자가 새 URL을 입력하고 이동 단추를 탭하면 사용자가 현재 검색 중인 URL이 포함된 해당 탭에 대한 새 NSUserActivity 항목이 만들어집니다.

핸드오프 앱 예제

다른 사용자의 디바이스 에 MonkeyBrowser 앱이 설치되어 있고, 동일한 사용자 계정을 사용하여 iCloud에 로그인되고, 동일한 네트워크에 있고, 위의 디바이스와 가까운 위치에 있는 경우 핸드오프 작업이 홈 화면(왼쪽 아래 모서리)에 표시됩니다.

왼쪽 아래 모서리의 홈 화면에 표시되는 핸드오프 활동

사용자가 핸드오프 아이콘을 위쪽으로 끌면 앱이 시작되고 해당 앱에 지정된 사용자 활동이 새 디바이스에서 NSUserActivity 계속됩니다.

새 디바이스에서 사용자 작업이 계속됨

사용자 활동이 다른 Apple 디바이스로 성공적으로 전송되면 보내는 디바이스 NSUserActivity 는 해당 메서드에 대한 NSUserActivityDelegate 호출 UserActivityWasContinued 을 수신하여 사용자 활동이 다른 디바이스로 성공적으로 전송되었음을 알릴 수 있습니다.

요약

이 문서에서는 사용자의 여러 Apple 디바이스 간에 사용자 작업을 계속하는 데 사용되는 핸드오프 프레임워크를 소개했습니다. 다음으로 Xamarin.iOS 앱에서 Handoff를 사용하도록 설정하고 구현하는 방법을 보여 줍니다. 마지막으로 사용 가능한 다양한 유형의 핸드오프 연속 작업과 핸드오프 모범 사례에 대해 설명했습니다.