ちょっと話題の記事

[iOS 7] [MultiTasking] バックグラウンドで定期的にフェッチ処理を実行する

2013.09.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

iOS7からバックグラウンドで定期的にフェッチ処理を実行することができるそうです。 この機能を利用すれば、ネットワークから定期的にコンテンツを取得する必要のあるアプリでは、バックグラウンドで新しいコンテンツがあるかどうかチェックしてダウンロードしたりできるようです。

バックグラウンドで定期的にフェッチ処理を実行するために必要な手順は以下の通りです。

  1. プロジェクト設定でBackground fetchを有効にする
  2. 最小のフェッチ間隔を設定する
  3. application:performFetchWithCompletionHandler:メソッドを実装する

1.プロジェクト設定でBackground fetchを有効にする

バックグラウンドでのフェッチ処理を使用するには、まずプロジェクトにその旨を設定する必要があります。この設定はXcode上から簡単に変更できます。
Xcodeを開いたら、プロジェクトナビゲータよりプロジェクトをクリックします。

ios-background-fetch-1

プロジェクトの設定画面を開いたら、タブメニューよりCapabilitiesをタップします。

ios-background-fetch-2

すると、Background Modesの項目があるので、スイッチをONにします。

ios-background-fetch-3

バックグラウンドで動作させる機能の中からBackground fetchにチェックを入れます。

ios-background-fetch-4

2.最小のフェッチ間隔を設定する

これだけではまだ使えません。UIApplicationにiOS7で新しく追加された- (void)setMinimumBackgroundFetchInterval:(NSTimeInterval)minimumBackgroundFetchIntervalメソッドで最小間隔を指定する必要があります。

このメソッドで設定できる設定値は以下の2つです。

UIApplicationBackgroundFetchIntervalMinimum
OSでサポートされている最小の間隔を指定する
UIApplicationBackgroundFetchIntervalNever
フェッチ処理を実行しない場合に指定する(デフォルト値)

この設定は、AppDelegateのapplication:didFinishLaunchingWithOptions:メソッドあたりで設定したりするようです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    return YES;
}

3.application:performFetchWithCompletionHandler:メソッドを実装する

あとは実際の処理を実行するだけです。処理はUIApplicationで定義されるapplication:performFetchWithCompletionHandler:に実装します。

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
	// コンテンツのダウンロード処理など

	if (success) {
		if (hasData) {
			completionHandler(UIBackgroundFetchResultNewData);
		} else {
			completionHandler(UIBackgroundFetchResultNoData);
		}
	} else {
		completionHandler(UIBackgroundFetchResultFailed);
	}
}

completionHandlerブロックはダウンロード操作の完了時に実行するブロックで、ダウンロード処理が完了したらこのブロックを実行します。このとき、引数に指定するUIBackgroundFetchResultの値は、処理結果に一番近いものを以下の3つの中から指定します。

UIBackgroundFetchResultNewData
新しいデータのダウンロードに成功した
UIBackgroundFetchResultNoData
ダウンロードすべきデータがなかった
UIBackgroundFetchResultFailed
データのダウンロードに失敗した

実行例

それでは簡単なサンプルを実装してみましょう。適当にサンプルプロジェクトを作成して上に書いたプロジェクトの設定を行いましょう。
設定したらAppDelegate.mを以下のように変更しましょう。

AppDelegate.m
#import "AppDelegate.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    return YES;
}

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    // バッジの数をインクリメント
    [UIApplication sharedApplication].applicationIconBadgeNumber++;

    // ローカル通知
    UILocalNotification *localNotification = [UILocalNotification new];
    localNotification.alertAction = @"OK";
    localNotification.alertBody = @"Called application:performFetchWithCompletionHandler:";
    localNotification.fireDate = [NSDate date];
    localNotification.timeZone = [NSTimeZone localTimeZone];
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

    // ダウンロード完了
    completionHandler(UIBackgroundFetchResultNoData);
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // バッジの数をリセット
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
}

@end

あとはいつも通り実行・・・と言いたいところですが、実行する前にスキーマに以下の設定をします。まず、以下のように「Edit Scheme...」よりスキーマの編集画面を表示します。

ios-background-fetch-5

デバッグ実行時の設定よりOptionsを開き、Background FetchLaunch due to a background fetch eventにチェックを入れます。こうすると、デバッグ実行時にアプリを起動せず、バックグラウンドのフェッチ処理を通るようにできます。なのでapplication:performFetchWithCompletionHandler:メソッドが実行されます。

ios-background-fetch-6

以下のように通知がくれば、バックグラウンドでのフェッチ処理が無事よばれています。

ios-background-fetch-7

まとめ

まず、気をつけなければならないのが、実行の間隔についてです。iOS7の定期的なバックグラウンドのフェッチ処理では、最小の間隔しか設定できないということです。ぴったり5秒ごとに実行するとかそういうことはできません。あくまで、「1度フェッチ処理が呼ばれたら、次回のフェッチ処理は設定した間隔が経過しないと呼ばれないようにするだけです。

実際に動かしてみると、最初は何回かくるけど次第に呼ばれなくなったり、呼ばれなくなってからアプリを起動すると呼ばれるようになったりとまちまちです。OS側でアプリの起動回数とかもみているかもしれません。

また、実際はこの機能とNSURLSessionを組み合わせてバックラウンドでダウンロード処理をしたりするみたいです。
その辺はまた次回!