Article

Downloading Files in the Background ファイルをバックグラウンドでダウンロードする

Create tasks that download files while your app is inactive. あなたのアプリが活動していない間にファイルをダウンロードする、そういうタスクを作成します。

Overview 概要

For long-running and nonurgent transfers, you can create tasks that run in the background. These tasks continue to run even when your app is suspended, allowing your app to access the downloaded file when the app is resumed. 長く動作するそして急を要しない転送のために、あなたはバックグラウンドで動作するタスクを作成できます。これらのタスクは、あなたのアプリが一時停止される時でさえ実行を継続して、アプリが再開される時に、あなたのアプリがそのダウンロードされるファイルにアクセスできるようにします。

Configure the Background Session バックグラウンドセッションを構成設定する

To perform a background download, configure a URLSession for background operation. Listing 1 demonstrates this process. バックグラウンドダウンロードを実行するには、URLSessionをバックグラウンド操作に対して構成設定してください。コード出力 1 は、この過程を実演します。

  1. Create a background URLSessionConfiguration object with the class method background(withIdentifier:) of URLSession, providing a session identifier that is unique within your app. Because most apps need only a few background sessions (usually one), you can use a fixed string for the identifier, rather than a dynamically generated identifier. The identifier doesn’t need to be unique globally. バックグラウンドURLSessionConfigurationオブジェクトをURLSessionのクラスメソッドbackground(withIdentifier:)で作成してください、あなたのアプリ内で特有であるセッション識別子を提供してください。ほとんどのアプリがほんの少しのバックグラウンドセッション(通常は1つ)を必要とすることから、あなたはその識別子に対して固定された文字列を使うことができます、動的に生成される識別子ではなく。識別子は、グローバルに特有である必要はありません。

  2. To have the system to wake up your app when a task completes and the app is in the background, make sure the sessionSendsLaunchEvents property is set to true (the default). システムにあなたのアプリを目覚めさせるには、あるタスクが完了するそしてそのアプリがバックグラウンドである時は、sessionSendsLaunchEventsプロパティがtrueに設定される(初期状態)ことを確実にしてください。

  3. For time-insensitive tasks, enable the isDiscretionary property, so the system can wait for optimal conditions to perform the transfer, such as when the device is plugged in or connected to Wi-Fi. 時間に無頓着なタスクに対して、isDiscretionaryプロパティを可能にしてください、それでシステムは転送を実行するのに最適な条件を待つことができます、たとえばデバイスがネット接続されるかまたはWi-Fiに接続される時に。

  4. Use the URLSessionConfiguration instance to create a URLSession instance. Provide a delegate, to receive events from the background transfer. URLSessionConfigurationインスタンスを使ってURLSessionインスタンスを作成してください。ある委任先を提供してください、バックグラウンド転送からのイベントを受け取るために。

Listing 1 Creating a background URL session コード出力 1 バックグラウンドURLセッションを作成する

private lazy var urlSession: URLSession = {
    let config = URLSessionConfiguration.background(withIdentifier: "MySession")
    config.isDiscretionary = true
    config.sessionSendsLaunchEvents = true
    return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()

Create and Schedule the Download Task ダウンロードタスクを作成および予定する

You create download tasks from the session with either the downloadTask(with:) method that takes a URL, or the downloadTask(with:) method that takes a URLRequest instance. You set properties on this method to help the system optimize its behavior. あなたはダウンロードタスクをセッションから、あるURLをとるdownloadTask(with:)メソッド、またはあるURLRequestインスタンスをとるdownloadTask(with:)メソッドのどちらかで、作成します。あなたは、このメソッド上でプロパティいくつかを設定して、システムがそれの挙動を最適化する助けとします。

  1. As shown in Listing 2, create a download task with downloadTask(with:). コード出力 2で示されるように、ダウンロードタスクをdownloadTask(with:)で作成してください。

  2. Optionally, set the earliestBeginDate property to schedule the download to begin at a particular point in the future. The download isn’t guaranteed to begin at precisely this time, but it won’t start sooner. 随意に、earliestBeginDateプロパティを設定して、そのダウンロードを未来の特定の時点で始めるように予定してください。ダウンロードは、正確にこの時間で開始すると保証されません、しかしそれは予定より早く始まりません。

  3. To help the system schedule network activity efficiently, set the properties countOfBytesClientExpectsToSend and countOfBytesClientExpectsToReceive. These values are best-guess upper bounds on the expected byte count, and should account for headers as well as body data. システムがネットワーク活動を効率的に予定するのを助けるために、プロパティcountOfBytesClientExpectsToSendcountOfBytesClientExpectsToReceiveを設定してください。これらの値は、予想バイト総数の最も有力な上限です、そしてヘッダだけでなくボディのデータも勘定に入れるべきです。

  4. To start the task, call resume(). タスクを開始するために、resume()を呼び出してください。

In Listing 2, the task is set to begin at least one hour in the future and is configured to send around 200 bytes of data and receive around 500 KB. コード出力 2では、タスクは最小では将来1時間で開始するよう設定されます、そして200バイトのデータを送信して500KBほど受信するように構成設定されます。

Listing 2 Creating a download task from a URL session コード出力 2 URLセッションからダウンロードタスクを作成する

let backgroundTask = urlSession.downloadTask(with: url)
backgroundTask.earliestBeginDate = Date().addingTimeInterval(60 * 60)
backgroundTask.countOfBytesClientExpectsToSend = 200
backgroundTask.countOfBytesClientExpectsToReceive = 500 * 1024
backgroundTask.resume()

Handle App Suspension アプリの一時停止を取り扱う

Different app states affect how your app interacts with the background download. In iOS, your app could be in the foreground, suspended, or even terminated by the system. See Managing Your App's Life Cycle for more information about these states. 異なるアプリ状態は、どのようにあなたのアプリがバックグラウンドダウンロードと相互作用するかに影響します。iOSでは、あなたのアプリはシステムによって前面、一時停止、または終了されることさえありえます。Managing Your App's Life Cycleをそれらの状態についてのさらなる情報として見てください。

If your app is in the background, the system may suspend your app while the download is performed in another process. In this case, when the download finishes, the system resumes the app and calls the UIApplicationDelegate method application(_:handleEventsForBackgroundURLSession:completionHandler:). This method receives the session identifier you created in Listing 1 as its second parameter. あなたのアプリがバックグラウンドであるならば、システムはあなたのアプリを、ダウンロードが別のプロセスにおいて実行される間に一時停止にするかもしれません。この場合には、タウンロードが終了する時、システムはアプリを再開して、UIApplicationDelegateのメソッドapplication(_:handleEventsForBackgroundURLSession:completionHandler:)を呼び出します。このメソッドは、あなたがコード出力 1 において作成したセッション識別子を、それの2番目のパラメータとして受け取ります。

This delegate method also receives a completion handler as its final parameter. Immediately store this handler wherever it makes sense for your app, perhaps as a property of your app delegate, or of your class that implements URLSessionDownloadDelegate. In Listing 3, this completion handler is stored in an app delegate property called backgroundCompletionHandler. この委任先メソッドはまた、完了ハンドラをそれの最終パラメータとして受け取ります。直接にこのハンドラをそれがあなたのアプリを理解するどこへでも格納してください、おそらくあなたのアプリ委任先の、またはURLSessionDownloadDelegateを実装するあなたのクラスのプロパティとして。コード出力 3 では、この完了ハンドラはbackgroundCompletionHandlerと呼ばれるアプリ委任先プロパティにおいて格納されます。

Listing 3 Storing the background download completion handler sent to the application delegate コード出力 3 アプリケーション委任先に送信されるバックグラウンドダウンロード完了ハンドラを格納する

func application(_ application: UIApplication,
                 handleEventsForBackgroundURLSession identifier: String,
                 completionHandler: @escaping () -> Void) {
        backgroundCompletionHandler = completionHandler
}

When all events have been delivered, the system calls the urlSessionDidFinishEvents(forBackgroundURLSession:) method of URLSessionDelegate. At this point, fetch the backgroundCompletionHandler stored by the app delegate in Listing 3 and execute it. Listing 4 shows this process. コード出力 4 は、このプロセスを示します。

Note that because urlSessionDidFinishEvents(forBackgroundURLSession:) may be called on a secondary queue, it needs to explicitly execute the handler (which was received from a UIKit method) on the main queue. urlSessionDidFinishEvents(forBackgroundURLSession:)が二次的なキュー上で呼び出されるかもしれないことから、それは明示的にハンドラ(それはUIKitメソッドから受け取られた)をメインキュー上で遂行する必要があることに注意してください。

Listing 4 Executing the background URL session completion handler on the main queue コード出力 4 バックグラウンドURLセッション完了ハンドラをメインキュー上で遂行する

func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    DispatchQueue.main.async {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
            let backgroundCompletionHandler =
            appDelegate.backgroundCompletionHandler else {
                return
        }
        backgroundCompletionHandler()
    }
}

Access the File, or Move It to a Permanent Location ファイルにアクセスする、またはそれを永久的な場所に移動する

Once your resumed app calls the completion handler, the download task finishes its work and calls the delegate’s urlSession(_:downloadTask:didFinishDownloadingTo:) method. At this point, the file is fully downloaded, and will be available until your delegate method returns. If you only need to read it once, you can access the file immediately in its temporary location. If you want to preserve the file, move it to a permanent location like the Documents directory, as described in Downloading Files from Websites. 一旦あなたの再開されたアプリが完了ハンドラを呼び出すならば、ダウンロードタスクはそれの仕事を終了します、そして委任先のもつurlSession(_:downloadTask:didFinishDownloadingTo:)メソッドを呼び出します。この時点で、ファイルは完全にダウンロードされます、そしてあなたの委任先メソッドが返るまでは利用可能でしょう。あなたがそれを一度だけ読み出す必要があるだけならば、あなたはそのファイルに直接にそれの一時的場所でアクセスできます。あなたがファイルを保存することを望むならば、それをDocumentsディレクトリのような永久的な場所に移動してください、ファイルをウェブサイトからダウンロードするで記述されるように。

Recreate the Session If the App Was Terminated アプリが終了されたならばセッションを再度作成する

If the system terminated the app while it was suspended, the system relaunches the app in the background. As part of your launch time setup, recreate the background session (see Listing 1), using the same session identifier as before, to allow the system to reassociate the background download task with your session. You do this so your background session is ready to go whether the app was launched by the user or by the system. Once the app relaunches, the series of events is the same as if the app had been suspended and resumed, as discussed earlier in Handle App Suspension. システムがそのアプリをそれが一時停止された間に終了するならば、システムはそのアプリをバックグラウンドで再起動します。あなたの起動時準備の一部として、バックグラウンドセッションを再作成して(コード出力 1を見てください)、前と同じセッション識別子を使用することで、システムがバックグラウンドタスクをあなたのセッションと再度結び付けられるようにします。あなたがこれをすることで、あなたのバックグラウンドセッションは、アプリがユーザによって起動されようとシステムによってであろうと、すぐに使えます。一旦アプリが再起動するならば、一連の出来事は、アプリが一時停止および再開された場合と同じです、より前にアプリの一時停止を取り扱うで議論されたように。

Comply with Background Transfer Limitations バックグラウンド転送制限に従う

With background sessions, the actual transfer is performed by a process that is separate from your app’s process. Because restarting your app’s process is fairly expensive, a few features are unavailable, resulting in the following limitations: バックグラウンドセッションでは、実際の転送は、あなたのアプリのもつプロセスから独立するあるプロセスによって実行されます。あなたのアプリのもつプロセスを再始動することはかなり高くつくことから、少数の機能は利用できません、結果として以下の制限になります:

  • The session must provide a delegate for event delivery. (For uploads and downloads, the delegates behave the same as for in-process transfers.) セッションは、ある委任先をイベント配送のために提供する必要があります。(アップロードとダウンロードに対して、委任先は進行中の転送と同じに振る舞います。)

  • Only HTTP and HTTPS protocols are supported (no custom protocols). HTTPとHTTPSプロトコルだけがサポートされます(あつらえのプロトコルはだめです)。

  • Redirects are always followed. As a result, even if you have implemented urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:), it is not called. リダイレクトは、常に対応されます。結果として、たとえあなたがurlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)を実装したとしても、それは呼び出されません

  • Only upload tasks from a file are supported (uploads from data instances or a stream fail after the app exits). ファイルからのアップロードタスクだけがサポートされます(データインスタンスまたはストリームからのアップロードはアプリが終了する後に失敗します)。

Use Background Sessions Efficiently バックグラウンドセッションを効率的に使う

When the system resumes or relaunches your app, it uses a rate limiter to prevent abuse of background downloads. When your app starts a new download task while in the background, the task doesn't begin until the delay expires. The delay increases each time the system resumes or relaunches your app. システムがあなたのアプリを再開または再起動する時、それはある比率制限を使ってバックグラウンドダウンロードの乱用を防ぎます。あなたのアプリが新しいダウンロードタスクを開始する一方でバックグラウンドにある場合、そのタスクは延期が満了するまで始まりません。延期は、システムがあなたのアプリを再開または再起動するごとに増大します。

As a result, if your app starts a single background download, gets resumed when the download completes, and then starts a new download, it will greatly increase the delay. Instead, use a small number of background sessions — ideally just one — and use these sessions to start many download tasks at once. This allows the system to perform multiple downloads at once, and resume your app when they have completed. 結果として、あなたのアプリがある単一のバックグラウンドダウンロードを開始するならば、ダウンロードが完了する時に再開されます、そしてそれから新しいダウンロードを始めます、それは大幅に延期を増やすでしょう。代わりに、少ない数のバックグラウンドセッションを使ってください — 理想的にはただ1つだけ — そしてそれらセッションを使って多くのダウンロードタスクを一度に開始してください。これは、システムに複数のダウンロードを一度に実行させます、そしてそれらが完了する時にあなたのアプリを再開します。

Keep in mind, though, that each task has its own overhead. If you find you need to launch thousands of download tasks, change your design to perform fewer, larger transfers. 心に留めておいてください、各タスクはそれ自身の経費がかかることを。あなたが何千というダウンロードタスクを起動する必要があることに気づくならば、あなたの設計を変更することで、より少しの、より多くの転送を実行してください。

See Also 参照

Downloading ダウンロード