ネイティブアプリケーションのHandoff
サンプルコード
https://github.com/sonsongithub/HandoffSample
ネイティブアプリケーション同士のHandoff - Xcodeの設定
ここまでで,NSUserActivityクラスのwebpageURLを使ったSafariへの簡単なURLの受け渡しだけを実装した. 次に,ネイティブアプリケーション同士のHandoffを実装してみる. 送信側は特別なことをする必要はないが,受信側はInfo.plistで処理するHandoffの識別子を明示する必要がある. Info.plistにNSUserActivityTypesキーでArrayを追加する. その配列にHandoffの識別子を逆ドメイン名フォーマットで追加する.
これでアプリケーションは,Handoffの受け取りが可能となる.
OSXの場合
OSXの場合もiOSと同様にInfo.plistに識別子を追加する. さらに,OSXであってもアプリケーションに署名をしなければ,Handoffを利用できない. OSXは設定しておけば,未署名のバイナリも実行できてしまうので注意を要する. 署名は,”Mac App 配布用証明書(Mac App Store)”, “Mac インストーラ配布用証明書(Mac App Store)”, “Developer ID アプリケーション証明書(Mac アプリケーション)”, “Developer ID インストーラ証明書 (Mac アプリケーション)”のどれでもいいようだ. しかしながら,筆者は,Developer ID アプリケーション証明書(Mac アプリケーション)による署名しかテストしていない.
SafariからネイティブアプリケーションをHandoffで起動する場合
このためには,JSONファイルへの署名等他に必要が作業が多い. 後日別エントリとして詳しく解説する.
送信側のコード
以後,特にiOSベースで解説する. 本質的なコードや挙動に関する議論は,OSXもiOSも変わらないはずである.
@interface ViewController () {
NSUserActivity *_activity;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_activity = [[NSUserActivity alloc] initWithActivityType:@"com.sonson.HandoffSample"];
_activity.title = @"Browsing";
_activity.userInfo = @{
@"Key" : @"information from the other device"
@"URL" : @"http://www.apple.com"
};
[_activity becomeCurrent];
}
@end
NSUserActivityのuserInfoにNSDictionaryで渡したい情報をコピーすればよい. いたって簡単だ.
受信側のコード
受け取り側のコードは,UIApplicationDelegate上で実装する.
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)activityType {
NSLog(@"application:willContinueUserActivityWithType:");
if ([activityType isEqualToString:@"com.sonson.HandoffSample"])
return YES;
return NO;
}
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType
error:(NSError *)error {
NSLog(@"application:didFailToContinueUserActivityWithType:error:");
NSLog(@"%@", error);
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler {
NSLog(@"application:continueUserActivity:restorationHandler:");
restorationHandler(@[]);
return YES;
}
まず,application:willContinueUserActivityWithType:
は,Handoffの識別子を受け取り,処理を行うかを判定する.
状況が悪かったりするときは,ここでNO
を返して,Handoffを実行しないようにすればいいだろう.
application:didFailToContinueUserActivityWithType:error:
は,,Handoffを受け取ったが処理に失敗した場合に呼び出される.
このメソッドがどういったときに呼ばれるかわかりにくい(それこそベータ版の時はこのメソッドしか呼ばれないことが多々あったが).
application:continueUserActivity:restorationHandler:
最後のapplication:continueUserActivity:restorationHandler:
で,具体的にHandoff経由で渡されたデータ等に基づいて,ビューの再構成を実行する.
引数で渡されるNSUserActivity
オブジェクトにHandoffに関する情報が保持される.
ややこしいのが,最後のrestorationHandler
だ.
iOS8以降,UIResponder
クラスにrestoreUserActivityState:
メソッドが追加された.
このメソッドは,Handoffによる状態復元を実行するためにある.
restorationHandler
メソッドの引数にUIResponder
のサブクラスのオブジェクトの配列を渡すと,その順番で,restoreUserActivityState:
メソッドが配列の各オブジェクト毎にコールされる.
iPadのメールアプリのような2ペインのビュー構成だった場合,以下のような状態復元になる.
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler {
// get or create view controller objects.
id viewControllerA = nil; // left pain
id viewControllerB = nil; // right pain
// start to restore
restorationHandler(@[viewControllerA, viewControllerB]);
return YES;
}
@implementation ViewControllerAClass
- (void)restoreUserActivityState:(NSUserActivity *)activity {
// restore left pain view using NSUserActivity object
}
@end
@implementation ViewControllerBClass
- (void)restoreUserActivityState:(NSUserActivity *)activity {
// restore right pain view using NSUserActivity object
}
@end
すでに公開中のアプリケーションであれば,少なからず状態復元のコードは実装されていると考えるので,既存のコードがあるのなら,その復旧コードをapplication:continueUserActivity:restorationHandler:
から呼び出せばいいだろう.
既存のコードでは対応できない復元項目があるのなら,このHandoffの仕組みを利用することも検討してもいいだろう.
2tch ネイティブアプリケーション同士のHandoffの例
webpageURL vs ネイティブアプリケーション同士のHandoff
ネイティブアプリケーション同士のHandoffは,受信側,送信側ともにアプリケーションがデバイスにインストールされていなければ,当然動作しない.
そういうときのために,webpageURL
とuserInfo
を両方入れておけばよい.
@interface ViewController () {
NSUserActivity *_activity;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_activity = [[NSUserActivity alloc] initWithActivityType:@"com.sonson.HandoffSample"];
_activity.webpageURL = [NSURL URLWithString:@"http://hoge.com/handoffSupport.html"];
_activity.title = @"Browsing";
_activity.userInfo = @{
@"Key" : @"information from the other device"
@"URL" : @"http://www.apple.com"
};
[_activity becomeCurrent];
}
@end
そうすると,Handoffの仕組みでアプリケーションがインストールされていない場合は,SafariにwebpageURL
が渡される.
このとき,webpageURL
にHandoffのサポートページやアプリケーションのダウンロードやインストールのページへのURLを渡したりすればユーザにスムーズにHandoffの仕組みを解説できる.
ブラウザベースのアプリもネイティブアプリもあるサービスならば,ネイティブアプリ向けの情報をuserInfoに保管しておき,ブラウザ用のURLをwebpageURL
に渡せばいいだろう.