iOS 7のための開発ノウハウ #1:iOS 7でのバックグラウンド処理について

こんにちは、メディアプローブのモバイル・アプリケーション部です。社内では自主的にチームが集まっては定期的な勉強会を開催しています。ここ最近の内容は、既存のiOSアプリケーションをiOS 7に対応した新しいバージョンに改良するために、iOS 7の新機能や新しいUIデザインにフォーカスしたものです。

このブログでは、iOS 6以前のアプリケーションをiOS 7に最適化したいという方々に向けて、我々の勉強会の成果を発表していきます。折しも、2014年2月から、Xcode 5を使ってiOS 7に最適化したアプリケーションでなければApp Storeで販売・配布できないというお達しがアップルからありました。もちろん、iOS 7への対応作業や新規開発をメディアプローブで承っていますので、このブログがお声がけいただくきっかけになればとも考えています。

では、早速一回目のテーマに入りましょう。

iOS 7でのバックグラウンド処理

iOS 7のアプリケーション設計・開発を行なう際、最も応用範囲の広い新機能のひとつが「Background Fetch」(バックグラウンド・フェッチ)です。iOSは通常、前面に来て操作できる状態にあるアプリケーションを「フォアグラウンド」とし、ホームボタンを一度押してOSに戻ったり、ホームボタンを2度押しした後、別のアプリケーションに画面を切り替えた際に、それまで前面にあったアプリケーションは「バックグラウンド」となります。

ただ、システム内部ではもっと細かい状態(ステート)の管理が行なわれています。下記の図にそれを示します。

iOSアプリケーションのステート遷移

iOSアプリケーションのステート遷移

このステートにおいてバックグラウンドは、「アプリケーションは背面に送られたけど、動作はしている」という状態であり、iOS 6以前でもいくつかの限られた処理(位置情報の取得、オーディオ再生など)はバックグラウンドで行なうことができました。

iOS 7のBackground Fetchは、バックグラウンドでデータを取得し、それに応じた処理を行なうことができるという新機能です。これにより、例えば弊社が提供しているニュースリーダーAppであれば、サーバーに対して定期的に記事を確認しに行き、新着記事があればニュース一覧画面の状態をバックグラウンドで更新。ユーザーが再びアプリケーションをフォアグラウンドにした時には、常に最新のニュース記事が読める状態になっている、ということが実現できます。

アプリケーション設計上の注意点

バックグラウンドでデータを取得するタイミングはiOSが制御し、一回のバックグラウンド処理の時間は30秒と決められています。

ユーザー観点ではデバイスのバッテリー消費量を極力抑えられるというメリットがありますが、アプリケーション提供者の観点では、そうした制約の中でバックグラウンド処理がスムーズに実行されるよう、きちんと設計しなければなりません。なお、ダウンロードに長時間かかるような大容量データを取得する場合は、Background Fetchとは別にBackground Transferというデータ転送の仕組みが用意されています。

開発・コーディング

ここからは、実際にサンプル・プログラムを組みながら説明をしていきます。GitHubにプロジェクト・ファイルをアップしてありますので、Xcode 5をお持ちの方は是非ダウンロードしてみてください。

まず、Xcodeのターゲット設定にある「Capabilities」でBackground Modeを指定します。これによって、そのアプリケーションはバックグラウンド処理を行なうタイプのアプリケーションとなります。

Xcodeのターゲット設定画面

Xcodeのターゲット設定画面

実際のコーディングでは、まず以下の一文でBackground Fetchを定義します。


[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

setMinimumBackgroundFetchIntervalへの引数を以下のいずれかに設定することで、バックグラウンド処理のタイミングをOSに宣言することができます。

  • UIApplicationBackgroundFetchIntervalMinimum (OSが定める最小間隔)
  • UIApplicationBackgroundFetchIntervalNever (バックグラウンド処理を行なわない)
  • もしくは、任意のインターバルタイム

これを AppDelegate.m 内の didFinishLaunchingWithOptions で設定します。


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	// Background Fetchの準備
	[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
	return YES;
}

そして – application:performFetchWithCompletionHandler: でバックグラウンド処理の中身を実装していきます。


- (void)application:(UIApplication*)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
	if ([UIApplication sharedApplication].backgroundRefreshStatus != UIBackgroundRefreshStatusAvailable) return;
	// for background fetch
	[self _checkNewDataWithCompletionHandler:^(BOOL available) {
		completionHandler(available ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultNoData);
	}];
}

処理後は速やかにcompletionHandlerを必ず実行します。こうすることで正常にバックグラウンド処理が動作した後に、OSは処理後の画面スナップショットを自動生成し、タスク・スイッチャー画面(ホームボタンを2度押し)に表示される画面を更新します。

iOS 6とiOS 7の両方を動作サポートするアプリケーションの場合、Background Fetchは当然iOS 7のみで機能するようにコーディングしなければなりません。そこで、以下のようなif判定文によって、Background Fetchをサポートした環境のみ設定を定義するというテクニックを使います。


if ([[UIApplication sharedApplication] respondsToSelector:@selector(setMinimumBackgroundFetchInterval:)]) {
	[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:60*60*3]; // every three hours
}

以上がBackground Fetchの概要および実装方法となります。

Comments are closed.