FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

マルチタスク/非マルチタスクでのhandleOpenURLの制御

konoです。iPhoneと言うかiOSネタです。

iOSではURLスキーマでアプリのコールが出来ますが、コール後に呼ばれるメソッド
OSのマルチタスク対応の有無によって変わる場合があります。

条件としては「application:didFinishLaunchingWithOptions」と「application:handleOpenURL」を
セットで使っていることで、「application:didFinishLaunching」と「application:handleOpenURL」の
セットの場合は気にしないでいいようです。

どうも「didFinishLaunchingWithOptions」を利用するとiOS3やiOS4でもiPod 2GやiPhone 3G
と言った非マルチタスクの場合、URLスキーマでアプリを呼び出した際に「handleOpenURL」が
宣言されていてもコールせず、「didFinishLaunchingWithOptions」がコールされるようです。


これは「handleOpenURL」はアプリがバックグラウンドに存在する場合のみにコールされる
という仕様のためです。
マルチタスクの場合、アプリがバックグラウンドに存在するということはないため、
「handleOpenURL」をコールする条件を満たしません。

これがマルチタスクの場合、アプリがバックグラウンドに存在すれば
「didFinishLaunchingWithOptions」はコールされず、「handleOpenURL」のみがコールされます。

ただし、「didFinishLaunchingWithOptions」には、バックグラウンド以外からの起動で返り値が
YESの場合は「handleOpenURL」をコールし、NOの場合はコールしないという仕様があります。

ですので、非マルチタスクの場合は返り値にYESを指定すれば「handleOpenURL」が
コールされそうなものですが、どうもそのように動いてくれません。

まとめると
[マルチタスクでURLスキーマで起動]
・ アプリがバックグラウンドにある場合は「handleOpenURL」のみをコール
・ アプリがバックグラウンドにない場合は「didFinishLaunchingWithOptions」をコール
「didFinishLaunchingWithOptions」の返り値にYESを設定すると、「handleOpenURL」をコール

[非マルチタスクでURLスキーマで起動]
・ 「didFinishLaunchingWithOptions」をコール
「didFinishLaunchingWithOptions」の返り値にYESを設定しても、「handleOpenURL」をコールしない

では、マルチタスク/非マルチタスクの両方で「handleOpenURL」をコールするには
どうすればいいかと言うと、端末とOSのバージョンを確認し、非マルチタスクの場合は
「didFinishLaunchingWithOptions」の中で「handleOpenURL」をコールする処理を
追加するという方法で対処できます。

実際には以下の様な感じになります。

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

	NSString *curSysVer = [[UIDevice currentDevice] systemVersion];

	size_t size = nil;

	// システムコントローラからハードウェア情報を取得
	sysctlbyname("hw.machine",NULL,&size,NULL,0);
	char *machine = malloc(size);
	sysctlbyname("hw.machine",machine,&size,NULL,0);
	NSString *platform = [NSString stringWithCString:machine];
	free(machine);

	if(([curSysVer hasPrefix:@"3"]
         || [platform isEqualToString:@"iPhone1,2"]
         || [platform isEqualToString:@"iPod2,1"])
	   && [launchOptions objectForKey:
          UIApplicationLaunchOptionsURLKey] != nil){
		[self application:application
               handleOpenURL:[launchOptions
          objectForKey:UIApplicationLaunchOptionsURLKey]];
	}

// 起動時の処理

}


条件式でiOS3 か iPhone 3GiPod 2G を調べ、「didFinishLaunchingWithOptions」の引数である
「launchOptions」 にキー:「UIApplicationLaunchOptionsURLKey」の値が存在するか否かで、
URLスキーマでアプリが起動されたかを判断します。
URLスキーマで起動された場合、「launchOptions」 のキー:「UIApplicationLaunchOptionsURLKey」
には、呼び出されたURLが格納されています。

注意しないといけないことは、「handleOpenURL」をコールした後は当然
「didFinishLaunchingWithOptions」の後続の処理が実行されるということです。

これがマルチタスクの処理フローと異なりややこしい点なのですが、

マルチタスクの場合 : 「didFinishLaunchingWithOptions」 → → 「handleOpenURL」

となりますが

マルチタスクで上記対応の場合 : 「didFinishLaunchingWithOptions」(handleOpenURLをコール)
→ 「handleOpenURL」(呼び出し元に戻る) → 「didFinishLaunchingWithOptions」

となります。

このため、非マルチタスクの場合は後続処理を制御する様な仕組みを入れないと、
想定していない処理が走ることがあり得るので注意が必要です。