RORK LABEN
FUNDING — Rorkの$15MシードはLeft Lane Capital主導で、Peak XV・True Ventures・Goodwater・a16z Speedrunが参加していますGROWTH — Rorkは月間74.3万訪問・成長率85%と、利用の伸びが続いていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — HealthKit・Core ML・Dynamic Islandなど、React Nativeでは届きにくい領域に踏み込めますMARKET — AppleもXcode 27でエージェント型コーディングを推進し、AIがネイティブ開発を担う流れが加速していますMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測していますFUNDING — Rorkの$15MシードはLeft Lane Capital主導で、Peak XV・True Ventures・Goodwater・a16z Speedrunが参加していますGROWTH — Rorkは月間74.3万訪問・成長率85%と、利用の伸びが続いていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — HealthKit・Core ML・Dynamic Islandなど、React Nativeでは届きにくい領域に踏み込めますMARKET — AppleもXcode 27でエージェント型コーディングを推進し、AIがネイティブ開発を担う流れが加速していますMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測しています
記事一覧/アプリ開発
アプリ開発/2026-07-03上級

Rork Max アプリでダウンロードをアプリ終了後も続ける — バックグラウンド URLSession の設計と再起動復帰

Rork Max のネイティブ Swift アプリで、アプリがサスペンド・終了されてもダウンロードを OS 側で継続させるバックグラウンド URLSession の設計を解説します。再起動復帰・resumeData・isDiscretionary の実測挙動まで動くコードで示します。

Rork Max207URLSessionバックグラウンド転送2Swift35iOS96ダウンロード

プレミアム記事

壁紙アプリで高解像度の画像パックを配信し始めた頃、レビュー欄に「ダウンロードが毎回途中で止まる」という報告が続いたことがあります。調べてみると、ユーザーはダウンロード開始後すぐホーム画面へ戻っており、およそ30秒後にアプリがサスペンドされた時点で転送が打ち切られていました。個人開発でサーバー側のログだけを眺めていても、この切断は「クライアント都合の切断」としか見えません。私自身、原因の切り分けに数日を費やしました。

答えはアプリ側の設計にありました。標準の URLSession はアプリのプロセスと運命を共にします。アプリが止まれば転送も止まる。これを避けるには、転送そのものを OS のデーモンに委ねる「バックグラウンドセッション」へ切り替える必要があります。

Rork Max はネイティブ Swift を生成するため、この仕組みをそのまま使えます。ただし通常のセッションと同じ感覚で書くと実行時エラーやファイル消失につながる制約がいくつもあります。以下、私が壁紙アプリの追加コンテンツ配信で実際に組んだ構成をもとに、設計の勘所を順に追っていきます。

転送を持つのはアプリではなく nsurlsessiond

バックグラウンドセッションの本質は「転送の実行主体がアプリのプロセス外に移る」ことです。実体は nsurlsessiond というシステムデーモンで、アプリがサスペンドされても、メモリ不足で終了されても、転送は OS 側で進み続けます。

通常セッションとの違いを先に整理しておきます。

観点通常セッションバックグラウンドセッション
転送の実行主体アプリのプロセス内nsurlsessiond(プロセス外)
アプリのサスペンド後転送は打ち切られる転送は継続する
アプリの終了後転送は消える転送は継続し、完了時にアプリが再起動される
completion handler 型の API使える使えない(実行時エラー)
delegate任意必須
dataTask使える原則不可(download / upload タスクのみ)

表の下2行が最初のつまずきどころです。session.downloadTask(with: url) { location, response, error in ... } のようなクロージャ渡しの API は、バックグラウンド構成のセッションでは呼んだ瞬間に例外で落ちます。アプリが生きていない間に完了する可能性がある以上、クロージャを保持し続けられないためで、すべての結果は delegate 経由で受け取る設計が強制されます。

セッションの生成と3つの制約

まずマネージャをシングルトンで用意します。identifier が同じセッションを複数生成すると挙動が不定になるため、生成箇所を一つに絞るのが安全です。

// BackgroundDownloadManager.swift
import Foundation
 
final class BackgroundDownloadManager: NSObject {
    static let shared = BackgroundDownloadManager()
    static let sessionIdentifier = "com.example.app.asset-downloads"
 
    /// アプリ再起動時に AppDelegate から渡される完了ハンドラ
    var backgroundCompletionHandler: (() -> Void)?
 
    private lazy var session: URLSession = {
        let config = URLSessionConfiguration.background(
            withIdentifier: Self.sessionIdentifier)
        config.sessionSendsLaunchEvents = true  // 終了後もアプリを起こす
        config.isDiscretionary = false          // 即時実行(後述)
        config.allowsCellularAccess = true
        return URLSession(configuration: config,
                          delegate: self,
                          delegateQueue: nil)
    }()
 
    func download(_ url: URL) {
        let task = session.downloadTask(with: url)
        // 期待サイズを伝えると OS のスケジューリングが安定します
        task.countOfBytesClientExpectsToReceive = 20_000_000
        task.resume()
    }
}

このコードが解決するのは「開始したダウンロードをアプリのライフサイクルから切り離す」ことです。押さえておく制約は3つあります。

第一に、delegate は初期化時にしか渡せません。後から差し替える API はないため、シングルトンの self を渡す構成が素直です。

第二に、sessionSendsLaunchEvents を true にしておかないと、アプリ終了後に転送が完了してもアプリは起こされません。デフォルトは true ですが、意図を明示するために書いています。

第三に、identifier はアプリ内で一意にします。機能ごとにセッションを分けたい場合は identifier も分け、それぞれの delegate を独立させます。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
アプリがサスペンド・終了されても OS 側で転送が続くダウンロード基盤を、動く Swift コードごと持ち帰れます
再起動をまたいで完了通知を受け取る handleEventsForBackgroundURLSession の配線と、SwiftUI アプリでの実装方法がわかります
resumeData による再開・一時ファイルの即時移動・isDiscretionary の実測挙動など、本番でつまずく箇所を先回りで塞げます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-06-16
初回起動の前に壁紙パックを落としておく — Rork Max と Background Assets の使いどころ
コンテンツの多いアプリは、初回起動時に空のグリッドを見せてしまいがちです。アプリ本体とは別枠でコンテンツを先回りして落とす Background Assets を、Rork(Expo)では届かない領域として Rork Max のネイティブ Swift でどう実装するか、運用判断まで含めてまとめます。
アプリ開発2026-06-16
Rork Max で WeatherKit を使う天気アプリ — 認証とアトリビューションの落とし穴
Rork Max が生成したネイティブ Swift アプリに WeatherKit を組み込む際、最初に詰まるのは認証とアトリビューションでした。トークンの扱い、レート上限、必須の出典表示まで、実装で確認した手順を整理します。
アプリ開発2026-07-02
Rork Max のゲームに Game Center を組み込む — 認証・リーダーボード・実績とスコア改ざんへの向き合い方
Rork Max で作った iOS ゲームに Game Center を組み込む手順を、認証・リーダーボード・実績の動く Swift コードと、スコア改ざんへの現実的な向き合い方まで含めて整理します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →