iOS 7のマルチタスク

iOS 7より前の開発者は、アプリケーションがバックグラウンドに残っている場合にできることについてかなり制限されていました。 VOIPおよび位置情報機能に加えて、バックグラウンドでコードを実行する唯一の方法は、数分以内に管理に限定されていたバックグラウンドタスクを使用することでした。 大きなビデオをアップロードしてオフラインで視聴したり、ユーザーの写真をサーバーに保存したりする場合は、作業の一部しかできませんでした。



IOS 7は、2つの新しいAPIを追加して、バックグラウンドでアプリケーションとコンテンツのユーザーインターフェイスを更新します。 1つ目のバックグラウンドフェッチ(バックグラウンドでの配信またはバックグラウンドの更新)では、定期的にネットワークから新しいコンテンツを受信できます。 第二に、リモート通知、この新機能はプッシュ通知を使用して、イベントが発生したときに通知します。 これらの新しいメカニズムはどちらも、アプリケーションのインターフェースを最新の状態に保つのに役立ち、ネットワークを介したアウトプロセスデータ転送(ダウンロードおよび転送)を実行できる新しいバックグラウンド転送サービスの作業を計画できます。



バックグラウンドフェッチとリモート通知は、アプリケーションが一時停止するまで30秒ごとに作業を完了するための単純なアプリケーションフックです。 これらは、プロセッサを集中的に使用する作業や長いタスクを対象とするものではなく、大きな映画のダウンロードやコンテンツの迅速な更新の実行など、次の長時間実行されるネットワーク要求のためのものです。



ユーザーの観点から見ると、マルチタスクの唯一の明らかな変更は、新しいアプリケーションスイッチャーです。これは、各アプリケーションのスナップショットをバックグラウンドで残したときの状態で表示します。 しかし、スナップショットを表示する理由があります-作業の完了後にアプリケーションのスナップショットを更新して、新しいコンテンツのプレビューを表示できるようになりました。 ソーシャルネットワーク、ニュース、または天気予報アプリケーションは、更新されたコンテンツを表示できるようになり、ユーザーはアプリケーションを開かないようになりました。 スナップショットを更新する方法については後ほど説明します。







背景スケッチ



バックグラウンドフェッチは、ソーシャルネットワーク、ニュース、天気予報アプリケーションなど、頻繁にコンテンツを更新するアプリケーションに最適な一種のスマートポーリングメカニズムです。 システムは、ユーザーの行動に基づいてアプリケーションを「起動」し、ユーザーがアプリケーションを起動する前に更新します。 たとえば、ユーザーが常に1日以内にアプリケーションを使用する場合、システムは認識して適応し、使用期間の前に更新を実行します。 バックグラウンド更新を組み合わせてバッテリーの使用量を削減します。更新中に新しいデータが利用できなかったと報告した場合、iOSはこの情報を使用して非アクティブ中の更新を回避できます。



バックグラウンドフェッチを提供する最初の手順は、情報シートのUIBackgroundModesキーの関数を使用することを示すことです。 これを行う最も簡単な方法は、Xcode 5プロジェクトエディターの[機能]タブを使用することです。このタブには、マルチタスク機能を便利に設定するためのバックグラウンドセクションモードが含まれています。



画像



別の方法として、キーを手動で編集できます。



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







次に、更新する頻度をiOSに指示します。



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







デフォルトでは、更新間隔は決してないため、時間間隔を設定する必要があります。そうしないと、アプリケーションがバックグラウンドで呼び出されることはありません。 UIApplicationBackgroundFetchIntervalMinimumの値は、アプリケーションができるだけ頻繁に起動するように制御するようシステムに要求しますが、これが必要でない場合は、独自の時間間隔を指定する必要があります。 たとえば、天気アプリは1時間ごとにのみ更新できます。 IOSは、バックグラウンド更新の間に少なくとも指定された時間間隔を待機します。



アプリケーションでユーザーの終了を許可し、そこに新しいデータがないことを知っている場合、minimumBackgroundFetchIntervalをUIApplicationBackgroundFetchIntervalNeverに戻すことで、良き市民になり、リソースを節約できます。



最後の手順は、アプリケーションに次のメソッドを実装することです。



 - (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURL *url = [[NSURL alloc] initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completionHandler(UIBackgroundFetchResultFailed); return; } //  response/data  ,      BOOL hasNewData = ... if (hasNewData) { completionHandler(UIBackgroundFetchResultNewData); } else { completionHandler(UIBackgroundFetchResultNoData); } }]; //   [task resume]; }
      
      







新しいコンテンツが利用可能かどうかを判断し、新しいコンテンツを処理し、ユーザーインターフェイスを更新する時間はわずか30秒です。 ネットワークからデータを取得してユーザーインターフェイスのサムネイルを描画するのに十分な時間があるはずですが、それ以上の時間はありません。 ネットワーク要求が完了し、ユーザーインターフェイスが更新されたら、完了ハンドラーを呼び出す必要があります。



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



実際のアプリケーションでは、completionHandlerを介してアプリケーションのサブコンポーネントにアクセスし、データを処理してユーザーインターフェイスを更新したときに名前を付ける必要があります。



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



ほとんどの場合、バックグラウンドでアプリケーションを起動するときに、フォアグラウンドで実行するのと同じジョブを実行しますが、UIApplicationからapplicationStateを調べることで、バックグラウンドの起動方法を確認できます。



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







バックグラウンド更新テスト



バックグラウンド更新をシミュレートするには、2つの方法があります。 Xcodeからアプリケーションを起動し、[デバッグ]の[Xcode]メニューの[バックグラウンド更新のシミュレート]をクリックすると、アプリケーションが動作する最も簡単な方法。



または、スキーマを使用して、Xcodeがアプリケーションを起動する方法を変更できます。 [Xcode]の[製品]メニュー項目で、[スキーム]、[スキームの管理]の順に選択します。 ここから、新しいスキームを編集または追加し、以下に示すようにeventcheckboxバックグラウンドの開始を確認します。



画像



削除された通知



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



リモート通知は、利用可能なコンテンツのラベルが付いた単なる定期的なプッシュ通知です。 バックグラウンドでユーザーインターフェイスを更新しているときに何かが起こったことをユーザーに知らせるプッシュアラートメッセージアラートを送信できます。 ただし、リモート通知は、アプリケーションのトリガーのインターフェイスまたはバックグラウンドを更新するためだけに使用されるアラートメッセージやサウンドを含まず、静かにすることもできます。 新しいコンテンツのダウンロードと処理が完了したら、ローカル通知を投稿できます。



サイレントプッシュ通知の速度は制限されているため、アプリケーションが必要とする数だけ送信することを恐れないでください。 IOSおよびAPNSサーバーは、それらが配信される頻度を制御します。また、送信しすぎても問題になりません。 プッシュ通知が調整されている場合、デバイスが次にアクティビティ検証パケットを送信するか、別の通知を受信するまで遅延する可能性があります。



リモート通知を送信する



リモート通知を送信するには、プッシュ通知ペイロードの利用可能なコンテンツのチェックボックスを選択します。 ほとんどのプッシュスクリプトとライブラリは既にリモート通知をサポートしています。 リモート通知を送信するときに、ペイロード通知にデータを含めることもできます。これにより、アプリケーションはイベントを参照できます。 これにより、いくつかのネットワーク要求を節約し、アプリケーションの機能を向上させることができます。



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



nomad-cliの一部としてヒューストンをインストールできます。



 gem install nomad-cli
      
      







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



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







その結果、次の通知を受け取ります。



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







IOS 7では、委任の新しいメソッドが追加されます。これは、コンテンツ-アクセス可能なキーを含むプッシュ通知を受信したときに呼び出されます。



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







繰り返しますが、アプリケーションはバックグラウンドで起動し、完了ハンドラーを呼び出す前に、コンテンツの更新とインターフェースの更新に30秒を与えます。 バックグラウンド更新で行ったように、迅速なネットワーク要求を行うことはできましたが、強力な新しい転送サービスを使用して大きなダウンロードタスクをキューに入れ、完了時にユーザーインターフェイスを更新する方法を見てみましょう。



NSURLSessionおよびバックグラウンド転送サービス



NSURLSessionはiOS 7の新しいクラスですが、リンクの新しいテクノロジーも指します。 NSURLConnectionを置き換えるように設計されており、NSURL、NSURLRequest、NSURLResponseなどの使い慣れた概念とクラスが保持されます。 NSURLConnectionの代わりのNSURLSessionTaskを使用して、ネットワーク要求を作成し、その応答を処理します。 セッションタスクには、データ、ダウンロード、アップロードの3つのタイプがあります。



NSURLSessionは、これらのNSURLSessionTasksの1つ以上を調整し、作成されたNSURLSessionConfigurationに従って動作します。 同じ構成に関連するグループタスク用に複数のNSURLSessionを作成できます。 バックグラウンド転送サービスと対話するには、[NSURLSessionConfiguration backgroundSessionConfiguration]を使用してセッション構成を作成します。 バックグラウンドセッションで追加されたタスクは外部プロセスで起動され、アプリケーションが一時停止、クラッシュ、または強制終了されても続行されます。



NSURLSessionConfigurationを使用すると、デフォルトのHTTPヘッダーの設定、キャッシュポリシーの構成、携帯電話の使用制限などを行うことができます。 1つのオプションは、システムが最適なパフォーマンスのためにタスクをスケジュールできるようにする任意フラグです。 これは、デバイスに十分な電力がある場合にのみ転送がWi-Fiを経由することを意味します。 バッテリー残量が少ない場合、または携帯電話のみが使用可能な場合、タスクは機能しません。 随意フラグは、セッション構成オブジェクトがbackgroundSessionConfiguration:メソッドを呼び出して構築された場合、およびアプリケーションがフォアグラウンドにある間にバックグラウンド更新が実行されている場合にのみ影響します。 送信がバックグラウンドで開始される場合、送信は常に任意モードで動作します。



NSURLSessionとバックグラウンド転送サービスについて少し知ったので、リモート通知の例に戻って、バックグラウンド転送サービスのダウンロードをキューに入れるコードを追加しましょう。 ダウンロードが完了すると、ファイルが使用できることをユーザーに通知します。



NSURLSessionDownloadTask



まず、バックグラウンド転送サービスについて、リモート通知に連絡し、anNSURLSessionDownloadTaskをエンキューしましょう。 InbackgroundURLSessionでは、バックグラウンドセッション構成でNURLSessionを作成し、アプリケーションデリゲートをセッションデリゲートとして追加します。 同じIDを持つ複数のセッションに対するドキュメントのヒント。したがって、潜在的な問題を回避するためにdispatch_onceを使用します。



 - (NSURLSession *)backgroundURLSession { static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier]; session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; }); return session; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo[@"content-id"]; NSString *downloadURLString = [NSString stringWithFormat:@"http://yourserver.com/downloads/%d.mp3", [contentID intValue]]; NSURL* downloadURL = [NSURL URLWithString:downloadURLString]; NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; NSURLSessionDownloadTask *task = [[self backgroundURLSession] downloadTaskWithRequest:request]; task.taskDescription = [NSString stringWithFormat:@"Podcast Episode %d", [contentID intValue]]; [task resume]; completionHandler(UIBackgroundFetchResultNewData); }
      
      







NSURLSessionクラスメソッドを使用してロードタスクを作成し、後で使用するために説明のリクエストをカスタマイズします。 セッションのすべてのタスクはlimboで開始されるため、開始するにはタスクを呼び出す必要があることを覚えておく必要があります。

次に、NSURLSessionDownloadDelegateメソッドを実装して、ダウンロードが完了したときにコールバックを受信する必要があります。 セッションのライフサイクルで認証やその他のイベントを実行する必要がある場合は、NSURLSessionDelegateまたはNSURLSessionTaskDelegateメソッドを実装する必要がある場合もあります。



NSURLSessionDownloadDelegateのデリゲートメソッドは必要ありませんが、この例[NSURLSession downloadTask:didFinishDownloadingToURL:]でアクションを実行する必要があるのは1つだけです。 ダウンロードタスクが完了すると、ディスク上のファイルへの一時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つのメソッドを実装する必要があります。 以前のデリゲートコールバックとは異なり、セッションデリゲートとタスクデリゲートは複数のメッセージを受信できるため、アプリケーションデリゲートは2回呼び出されます。



NSURLSessionメッセージがデリゲートに送信される前に、アプリケーションデリゲートメソッドを使用してhandleEventsForBackgroundURLSession:が呼び出され、URLSessionDidFinishEventsForBackgroundURLSessionが後で呼び出されます。 最初のメソッドでは、バックグラウンドのcompletionHandlerを保存し、2番目のメソッドではこれを呼び出してユーザーインターフェイスを更新します。



 - (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 = [self backgroundURLSession]; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // Store the completion handler to update your UI after processing session events [self addCompletionHandler:completionHandler forSession:identifier]; } - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { NSLog(@"Background URL session %@ finished events.\n", session); if (session.configuration.identifier) { // Call the handler we stored in -application:handleEventsForBackgroundURLSession: [self callCompletionHandlerForSession:session.configuration.identifier]; } } - (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier { if ([self.completionHandlerDictionary objectForKey:identifier]) { NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } [self.completionHandlerDictionary setObject:handler forKey:identifier]; } - (void)callCompletionHandlerForSession: (NSString *)identifier { CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey: identifier]; if (handler) { [self.completionHandlerDictionary removeObjectForKey: identifier]; NSLog(@"Calling completion handler for session %@", identifier); handler(); } }
      
      







これは2段階のプロセスです。バックグラウンド転送が終了したときにまだフォアグラウンドにいない場合は、アプリケーションのユーザーインターフェイスを更新する必要があります。 さらに、アプリケーションがまったく実行されていない場合、iOSはそれをバックグラウンドで起動し、以前のアプリケーションとセッションは次の後に呼び出されるメソッドを委任します:didFinishLaunchingWithOptions:



設定と制限



バックグラウンド転送のパワーについて簡単に触れましたが、ドキュメントを調べて、ユースケースを最もよくサポートするNSURLSessionConfigurationオプションを検討する必要があります。 たとえば、NSURLSessionTasksリソースは、NSURLSessionConfigurationのtimeoutIntervalForResourcepropertyによるタイムアウトをサポートしています。 これは、コンテンツが限られた時間しか利用できない場合、または特定のTimeInterval内にコンテンツをダウンロードおよびアップロードできない場合、ユーザーに十分なWi-Fi帯域幅がないことを示す場合に使用できます。



さらに、タスクをダウンロードするために、NSURLSessionはダウンロードタスクを完全にサポートします。これにより、バックグラウンドでサーバーにビデオをアップロードし、iOS 6のようにユーザーがアプリケーションを動作させたままにする必要がなくなります。転送の完了時にアプリケーションがバックグラウンドで実行する必要がない場合、NSURLSessionConfigurationからsessionSendsLaunchEventsをNOに設定します。 システムリソースの効率的な使用は、iOSとユーザーの両方を満足させます。



最後に、バックグラウンドセッションの使用にはいくつかの制限があります。 デリゲートが必要とするため、onNSURLSessionコールバックメソッドに基づく単純なブロックは使用できません。 バックグラウンド転送はHTTPとHTTPSのみをサポートし、ユーザープロトコルは使用できません。 システムは使用可能なリソースに基づいて転送を最適化します。転送を常にバックグラウンドで強制的に進行させることはできません。

また、NSURLSessionDataTasksはバックグラウンドセッションではまったくサポートされていないことに注意してください。これらのタスクは、ロードや追加ではなく、短時間の短いリクエストにのみ使用してください。



まとめ



iOS 7の強力な新しいマルチタスクおよびネットワークインターフェースは、新規および既存のアプリケーションのあらゆる可能性を開きました。 ネットワーク経由でデータを転送し、データを更新する独自のプロセスの恩恵を受けることができるアプリケーションのユースケースを検討し、これらの素晴らしい新しいAPIを最大限に活用してください。 一般的に、アプリケーションがフォアグラウンドで実行されているかのようにバックグラウンドブロードキャストを実行し、ユーザーインターフェイスを適切に更新します。ほとんどの作業は既に行われています。



•適切な新しいAPIを使用して、アプリケーションを含めます。

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

•完了ハンドラーは、アプリケーションのユーザーインターフェイスのスナップショットを更新します。



All Articles