objc.io

iOS7以前は、開発者はアプリがフォアグラウンドを離れたときに何ができるかにかなり制限されていました。 VOIPとロケーションベースの機能は別として、バックグラウンドでコードを実行する唯一の方法は、数分間実行することに制限されたバックグラウンドタスクを使用することでした。 オフラインで見るために大きなビデオをダウンロードしたり、ユーザーの写真をサーバーにバックアップしたりする場合は、作業の一部しか完了できません。

iOS7では、アプリのUIとコンテンツをバックグラウンドで更新するための二つの新しいApiが追加されました。 最初のBackground Fetchでは、ネットワークから定期的に新しいコンテンツを取得できます。 2つ目のRemote Notificationsは、プッシュ通知を利用してイベントが発生したときにアプリに通知する新機能です。 これらの新しいメカニズムはどちらも、アプリのインターフェイスを最新の状態に保つのに役立ち、新しいバックグラウンド転送サービスで作業をスケジュー

バックグラウンドフェッチとリモート通知は、アプリが中断される前に作業を実行するための30秒の壁時計時間を持つ単純なアプリケーションデリゲートフックです。 大規模な映画のダウンロードやコンテンツの迅速な更新など、長時間実行されるネットワーク要求をキューに入れたりするためのものです。

ユーザーの視点から見ると、マルチタスクへの唯一の明白な変更は、各アプリのUIのスナップショットをフォアグラウンドを離れたときのように表示する新しいapp switcherです。 バックグラウンド作業を完了した後にアプリのスナップショットを更新し、新しいコンテンツのプレビューを表示できるようになりました。 ソーシャルネットワーキング、ニュース、天気アプリは、ユーザーがアプリを開くことなく、最新のコンテンツを表示できるようになりました。 スナップショットの更新方法については、後で説明します。

Background Fetch

Background Fetchは、ソーシャルネットワーキング、ニュース、天気アプリなど、コンテンツの更新が頻繁なアプリに最適なスマートポーリングメカニズムの一種です。 システムはユーザーの行動に基づいてアプリを起動し、ユーザーがアプリを起動する前にバックグラウンドフェッチをトリガーすることを目的としています。 たとえば、ユーザーが常に午後1時にアプリを使用している場合、システムは学習して適応し、使用期間の前にフェッチを実行します。 また、フェッチ中に新しいデータが利用できなかったと報告した場合、iOSはこの情報を使用して静かな時間にフェッチを回避することができます。

バックグラウンドフェッチを有効にする最初のステップは、info plistのUIBackgroundModesキーでこの機能を使用することを指定することです。 これを行う最も簡単な方法は、Xcode5のプロジェクトエディタの新しい機能タブを使用することです。

または、キーを手動で編集することもできます:

<key>UIBackgroundModes</key><array> <string>fetch</string></array>

次に、取得する頻度をiOSに伝えます:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ ; return YES;}

デフォルトのフェッチ間隔は決してないので、時間間隔を設定する必要があります。 値UIApplicationBackgroundFetchIntervalMinimumは、アプリの起動時にできるだけ頻繁に管理するようにシステムに要求しますが、不要な場合は独自の時間間隔を指定する必要があります。 たとえば、天気アプリは条件を1時間ごとにのみ更新する場合があります。 iOSは、少なくとも指定された時間間隔でバックグラウンドフェッチを待機します。

アプリケーションでユーザーのログアウトが許可されていて、新しいデータがないことがわかっている場合は、minimumBackgroundFetchIntervalUIApplicationBackgroundFetchIntervalNeverに戻して、善良な市民にし、リソースを節約

最後のステップは、アプリケーションデリゲートに次のメソッドを実装することです:

- (void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSURLSessionConfiguration *sessionConfiguration = ; NSURLSession *session = ; NSURL *url = initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = ; // Start the task ;}

これは、システムによって目覚めたときに作業を実行できる場所です。 新しいコンテンツが利用可能かどうかを判断し、新しいコンテンツを処理し、UIを更新するのに30秒しかかからないことを覚えておいてください。 これは、ネットワークからデータを取得し、UI用のいくつかのサムネイルを取得するのに十分な時間ですが、それ以上ではありません。 ネットワーク要求が完了し、UIが更新されたら、完了ハンドラーを呼び出す必要があります。

完了ハンドラには2つの目的があります。 まず、システムはプロセスによって使用される電力を測定し、渡されたUIBackgroundFetchResult引数に基づいて新しいデータが利用可能かどうかを記録します。 次に、完了ハンドラーを呼び出すと、UIのスナップショットが取得され、アプリスイッチャーが更新されます。 ユーザーがアプリを切り替えているときに、新しいコンテンツが表示されます。 この完了ハンドラーのスナップショット動作は、新しいマルチタスクApiのすべての完了ハンドラーに共通しています。

実世界のアプリケーションでは、アプリケーションのサブコンポーネントにcompletionHandlerを渡し、データを処理してUIを更新したときに呼び出す必要があります。

この時点で、iOSがバックグラウンドで実行されているときにアプリのUIをスナップショットする方法と、アプリケーションのライフサイクルがバックグラウンドフェッチでどのように動作するか疑問に思うかもしれません。 アプリが現在中断されている場合、システムはapplication: performFetchWithCompletionHandler:を呼び出す前にアプリを起動します。 アプリが実行されていない場合、システムはそれを起動し、application: didFinishLaunchingWithOptions:を含む通常のデリゲートメソッドを呼び出します。 UIが非表示で画面外に表示されることを除いて、ユーザーがSpringboardから起動した場合とまったく同じ方法で実行されるアプリと考えることができます。

ほとんどの場合、アプリケーションがバックグラウンドで起動するときとフォアグラウンドで起動するときと同じ作業を実行しますが、UiaapplicationのapplicationStateプ:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES;}

バックグラウンドフェッチのテスト

バックグラウンドフェッチをシミュレートするには、二つの方法があります。 最も簡単な方法は、Xcodeからアプリケーションを実行し、アプリの実行中にXcodeのDebugメニューのSimulate Background Fetchをクリックすることです。

または、スキームを使用してXcodeがアプリを実行する方法を変更することもできます。 Xcodeメニュー項目製品の下で、スキームを選択し、スキームを管理します。 ここから、新しいスキームを編集または追加し、以下に示すように背景フェッチイベントによる起動チェックボックスをオンにします。

リモート通知

リモート通知を使用すると、重要なイベントが発生したときにアプリに通知できます。 配信する新しいインスタントメッセージ、送信する最新のニュースアラート、またはユーザーのお気に入りのテレビ番組の最新エピソードをオフラインで リモート通知は、バックグラウンドフェッチ間の遅延が許容されない可能性がある散発的ではあるがすぐに重要なコンテンツに最適です。 アプリケーションは必要なときにのみ起動するため、リモート通知はバックグラウンドフェッチよりもはるかに効率的です。

リモート通知は、実際にはcontent-availableフラグが設定された通常のプッシュ通知です。 バックグラウンドでUIを更新している間に、何かが起こったことをユーザーに通知する警告メッセージを含むプッシュを送信することができます。 しかし、リモート通知は、アプリのインターフェイスを更新したり、バックグラウンド作業をトリガーするためにのみ使用される、警告メッセージや音を含 新しいコンテンツのダウンロードまたは処理が完了したら、ローカル通知を投稿することができます。

サイレントプッシュ通知はレートに制限があるため、アプリケーションが必要とする数だけ送信することを恐れないでください。 iOSとAPNSサーバーは、配信される頻度を制御し、送信する頻度が多すぎるために問題になることはありません。 プッシュ通知が調整されている場合は、次回デバイスがキープアライブパケットを送信するか、別の通知を受信するまで遅延する可能性があります。

リモート通知の送信

リモート通知を送信するには、プッシュ通知ペイロードにcontent-availableフラグを設定します。 Content-availableフラグは、ニューススタンドアプリに通知するために使用されるのと同じキーであるため、ほとんどのプッシュスクリプトとライブラリ リモート通知を送信するときに、アプリケーションがイベントを参照できるように、通知ペイロードに一部のデータを含めることもできます。 これにより、いくつかのネットワーク要求を節約し、アプリの応答性を向上させることができます。

開発中にNomad CLIのHoustonユーティリティを使用してプッシュメッセージを送信することをお勧めしますが、お気に入りのライブラリやスクリプトを使用

:

gem install nomad-cli

そして、Nomadに含まれるapnユーティリティで通知を送信します

# Send a Push Notification to your Deviceapn push <device token> -c /path/to/key-cert.pem -n -d content-id=42

ここで、-nフラグはcontent-availableキーを含める必要があることを指定し、-dはペイロードに独自のデータキーを追加することができます。

結果の通知ペイロードは次のようになります:

{ "aps" : { "content-available" : 1 }, "content-id" : 42}

iOS7では、content-availableキーを持つプッシュ通知を受信したときに呼び出される新しいapplicationデリゲートメソッドが追加されます:

- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Remote Notification userInfo is %@", userInfo); NSNumber *contentID = userInfo; // Do something with the content ID completionHandler(UIBackgroundFetchResultNewData);}

再び、アプリがバックグラウンドで起動され、完了ハンドラを呼び出す前に、新しいコンテンツを取得してUIを更新するために30秒が与えられます。 バックグラウンドフェッチの例で行ったように、迅速なネットワーク要求を実行できますが、強力な新しいバックグラウンド転送サービスを使用して、大規模なダウンロードタスクをキューに入れ、完了したときにUIを更新する方法を見てみましょう。

NSURLSession And Background Transfer Service

NSURLSession はiOS7の新しいクラスですが、Foundation networkingの新しい技術についても言及しています。 NSURLConnectionを置き換えるために、NSURLNSURLRequestNSURLResponseなどのよく知られた概念とクラスが保持されます。 ネットワーク要求を行い、その応答を処理するには、NSURLConnectionの置換NSURLSessionTaskを使用します。 セッションタスクにはdata、download、uploadの3種類があり、それぞれがNSURLSessionTaskに構文糖を追加するので、ユースケースに適したものを使用する必要があります。

NSURLSessionは、これらのNSURLSessionTaskのうちの一つ以上を座標とし、それが作成されたNSURLSessionConfigurationに従って動作します。 複数のNSURLSessionを作成して、同じ構成の関連タスクをグループ化することができます。 バックグラウンド転送サービスと対話するには、を使用してセッション構成を作成します。 バックグラウンドセッションに追加されたタスクは、外部プロセスで実行され、アプリが中断された場合、クラッシュした場合、または強制終了された場合でも続行されます。

NSURLSessionConfigurationデフォルトのHTTPヘッダーの設定、キャッシュポリシーの設定、携帯電話ネットワークの使用制限などを行うことができます。 一つのオプションは、システムが最適なパフォーマンスのためにタスクをスケジュールすることを可能にするdiscretionaryフラグです。 これが意味することは、デバイスが十分な電力を持っている場合にのみ、転送がWifi経由で行われるということです。 バッテリーが不足している場合、または携帯電話接続のみが利用可能な場合、タスクは実行されません。 discretionaryフラグは、セッション構成オブジェクトがbackgroundSessionConfiguration:メソッドを呼び出して構築されていて、アプリがフォアグラウンドにある間にバックグラウンド転送が開始された場合にのみ効果があります。 転送がバックグラウンドから開始された場合、転送は常に随意モードで実行されます。

さて、NSURLSessionについて少し知っていて、バックグラウンドセッションがどのように機能するかについては、リモート通知の例に戻り、バックグラウンド転送サービスのダウンロードをエンキューするコードを追加しましょう。 ダウンロードが完了すると、ファイルが使用可能であることをユーザーに通知します。

NSURLSessionDownloadTask

まず、リモート通知を処理し、バックグラウンド転送サービスにNSURLSessionDownloadTaskをエンキューしましょう。 backgroundURLSessionでは、バックグラウンドセッション構成を持つNURLSessionを作成し、アプリケーションデリゲートをセッションデリゲートとして追加します。 ドキュメントでは、同じ識別子を持つ複数のセッションをインスタンス化することを推奨しているため、潜在的な問題を回避するためにdispatch_once:

- (NSURLSession *)backgroundURLSession{ static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = ; session = ]; }); return session;}- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo; NSString *downloadURLString = ]; NSURL* downloadURL = ; NSURLRequest *request = ; NSURLSessionDownloadTask *task = downloadTaskWithRequest:request]; task.taskDescription = ]; ; completionHandler(UIBackgroundFetchResultNewData);}

NSURLSessionクラスメソッドを使用してダウンロードタスクを作成し、その要求を構成し、後で使用するための説明を提供します。 すべてのセッションタスクがsuspended状態で開始されるため、実際にタスクを開始するにはを呼び出すことを忘れないでください。

ここで、ダウンロードが完了したときにコールバックを受け取るためのNSURLSessionDownloadDelegateメソッドを実装する必要があります。 また、セッションのライフサイクルで認証やその他のイベントを処理する必要がある場合は、NSURLSessionDelegateまたはNSURLSessionTaskDelegateメソッドを実装する必要があります。 Appleのdocument Life Cycle of a URL Session with Custom Delegatesを参照して、すべてのタイプのセッションタスクのライフサイクル全体を説明する必要があります。

NSURLSessionDownloadDelegateデリゲートメソッドのどれもオプションではありませんが、この例でアクションを実行する必要があるのはだけです。 タスクのダウンロードが完了すると、ディスク上のファイルへの一時的なURLが提供されます。 このデリゲートメソッドから戻ったときに一時記憶域から削除されるため、ファイルをアプリのストレージに移動またはコピーする必要があります。

#Pragma Mark - NSURLSessionDownloadDelegate- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location); // Copy file to your app's storage with NSFileManager // ... // Notify your UI}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{}

バックグラウンドセッションタスクが完了したときにアプリがまだフォアグラウンドで実行されている場合は、上記のコードで十分です。 ただし、ほとんどの場合、アプリは実行されないか、バックグラウンドで中断されます。 このような場合は、システムがアプリケーションをウェイクできるように、2つのアプリケーションデリゲートメソッドを実装する必要があります。 以前のデリゲートコールバックとは異なり、セッションデリゲートとタスクデリゲートは複数のメッセージを受信する可能性があるため、ア Appデリゲートメソッドapplication: handleEventsForBackgroundURLSession:は、これらのNSURLSessionデリゲートメッセージが送信される前に呼び出され、その後URLSessionDidFinishEventsForBackgroundURLSessionが呼び出されます。 前者の方法では、backgroundcompletionHandlerを保存し、後者ではUIを更新するためにそれを呼び出します:

- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ // You must re-establish a reference to the background session, // or NSURLSessionDownloadDelegate and NSURLSessionDelegate methods will not be called // as no delegate is attached to the session. See backgroundURLSession above. NSURLSession *backgroundSession = ; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // Store the completion handler to update your UI after processing session events ;}- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ NSLog(@"Background URL session %@ finished events.\n", session); if (session.configuration.identifier) { // Call the handler we stored in -application:handleEventsForBackgroundURLSession: ; }}- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier{ if () { NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } ;}- (void)callCompletionHandlerForSession: (NSString *)identifier{ CompletionHandlerType handler = ; if (handler) { ; NSLog(@"Calling completion handler for session %@", identifier); handler(); }}

この2段階のプロセスは、バックグラウンド転送が完了したときにまだフォアグラウンドにいない場合に、アプリUIを更新するために必要です。 さらに、バックグラウンド転送が完了したときにアプリがまったく実行されていない場合、iOSはそれをバックグラウンドで起動し、前のapplicationデリゲートメ

設定と制限

バックグラウンド転送の力について簡単に触れましたが、ドキュメントを調べて、ユースケースを最もサポートするNSURLSessionConfigurationオプションを見てくださ たとえば、NSURLSessionTasksNSURLSessionConfigurationtimeoutIntervalForResourceプロパティを使用してリソースのタイムアウトをサポートします。 これを使用して、完全に放棄する前に転送が完了するまでの時間を指定できます。 コンテンツが限られた時間のみ利用可能である場合、または指定されたtimeInterval内でリソースのダウンロードまたはアップロードに失敗した場合、ユーザーが十分なWifi帯域幅を持っていないことを示している場合に、これを使用できます。

ダウンロードタスクに加えて、NSURLSessionはアップロードタスクを完全にサポートしているため、バックグラウンドでサーバーにビデオをアップロードし、iOS6で行われていたように、アプリを実行したままにする必要がなくなったことをユーザーに保証することができます。 転送が完了したときにアプリをバックグラウンドで起動する必要がない場合は、NSURLSessionConfigurationsessionSendsLaunchEventsプロパティをNOに設定するのがいいでしょう。 システムリソースの効率的な使用は、iOSとユーザーの両方を幸せに保ちます。

最後に、バックグラウンドセッションの使用にはいくつかの制限があります。 デリゲートが必要なため、NSURLSessionでは単純なブロックベースのコールバックメソッドを使用することはできません。 アプリをバックグラウンドで起動するのは比較的高価なので、HTTPリダイレクトは常に使用されます。 バックグラウンド転送サービスはHTTPとHTTPSのみをサポートし、カスタムプロトコルを使用することはできません。 システムは、利用可能なリソースに基づいて転送を最適化し、あなたはすべての回でバックグラウンドで進行するためにあなたの転送を強制するこ

また、NSURLSessionDataTasksはバックグラウンドセッションではまったくサポートされていないことに注意してください。

概要

iOS7の強力な新しいマルチタスクとネットワーキングApiは、新規および既存のアプリの両方の可能性の全範囲を開きます。 アウトプロセスのネットワーク転送や新鮮なデータの恩恵を受けるアプリのユースケースを検討し、これらの素晴らしい新しいApiを最大限に活用してくださ 一般に、アプリケーションがフォアグラウンドで実行されているかのようにバックグラウンド転送を実装し、適切なUIの更新を行い、ほとんどの作業

  • アプリのコンテンツに適切な新しいAPIを使用します。

  • 効率的であり、できるだけ早く完了ハンドラを呼び出します。

  • 完了ハンドラーは、アプリのUIスナップショットを更新します。

さらに読む

  • WWDC2013セッション”マルチタスクの新機能”

  • WWDC2013セッション”What’s New in Foundation Networking”

  • URL読み込みシステムプログラミングガイド

コメントを残す

メールアドレスが公開されることはありません。