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

75件のシェア(ちょっぴり話題の記事)

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を組み合わせてバックラウンドでダウンロード処理をしたりするみたいです。
その辺はまた次回!

  • ナナ

    ありがとうございます

    定期的というのは自動的にperformFetchWithCompletionHandlerをおこないますか?

    上記のコードを実行しますが、Debug -> simulate background fetchをクリックしないと、performFetchWithCompletionHandlerを実行しないです。

    • 平井 祐樹

      返信遅くなってしまい、申し訳ありません。。

      > 定期的というのは自動的にperformFetchWithCompletionHandlerをおこないますか?

      performFetchWithCompletionHandlerが呼ばれるタイミングは、iOSで自動的にスケジュールされます。

      iOSでは、Background fetchを有効にしたアプリが起動された日時や頻度から勝手にスケジュールするそうです。

      なので、デバッグ実行したら適当に起動・ホームボタンを繰り返していればそのうちperformFetchWithCompletionHandlerが呼ばれるようになるそうです!

      参考:http://www.slideshare.net/techblogyahoo/3-ios7-workshopmultitask-26997115