RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-05-07上級

Rorkアプリで「閉じてる間も最新」を本番品質で実装する — iOS BGTaskScheduler × Silent Push × Android WorkManager

Rorkで作ったアプリのバックグラウンド更新を、iOSのBGTaskScheduler・Silent Push、AndroidのWorkManagerで本番品質に仕上げるための実装ガイド。配信成功率の落とし穴と回復パターンまでを解説します。

Rork515BGTaskScheduler3Silent PushWorkManagerバックグラウンド更新2React Native209Expo149

プレミアム記事

「アプリを閉じている時間こそが、いちばん勝負どころなんですよ」と、ある先輩開発者から言われたことを今でもよく思い出します。私が個人開発を始めた頃、ユーザーがアプリを起動している時間にだけ気を配っていました。けれど数年運営してみて、本当に効くのは「閉じている間に何をしておけるか」だと痛感しました。たった3秒の起動直後に新着が見えるか、5秒経ってからやっと表示されるか。その差が翌日の継続率を1〜2ポイント動かしてしまうことを、ダッシュボードを見ながら何度も実感してきました。

Rorkで生成したアプリにこの「閉じてる間の更新」を組み込もうとすると、iOSとAndroidの仕様の違いに最初は戸惑います。Silent Pushを送ってもなぜか届かない、BGTaskSchedulerを登録したのに発火しない、AndroidのWorkManagerがDozeで止まってしまう。ドキュメント通りに書いたつもりなのに、本番では半分くらいしか動いていありません。私自身、累計5,000万ダウンロードのアプリを運用するなかで、このバックグラウンド処理の落とし穴に何度もぶつかってきました。

ここで扱うのはその経験を整理しながら、Rorkで作ったアプリに「閉じてる間も最新」を本番品質で実装する手順を、コードと運用パターンの両面からまとめます。

iOSとAndroidのバックグラウンド実行モデルを正確に理解する

最初にしておきたいのが、両OSの「バックグラウンドで何ができるか」の違いを正確に把握することです。ここを曖昧にしたまま実装を進めると、片方のOSでは動くけれどもう片方では沈黙する、という状態になります。

iOSのバックグラウンド更新は、大きく3つの経路があります。BGAppRefreshTaskは短時間(30秒前後)の軽い更新を、システムが学習したユーザーの利用パターンに合わせて発火します。BGProcessingTaskは数分〜数時間の重い処理(端末の充電中・Wi-Fi接続中などの条件付き)を担います。そしてSilent Push、つまりapns-push-type: backgroundかつcontent-available: 1を含むAPNS通知が、サーバ側からトリガーする更新の入口です。これらは互いに排他ではなく、実用上は3つを併用して「いずれかが届く確率を上げる」設計になります。

一方Androidは、API 31(Android 12)以降のWorkManagerが事実上の正解になっています。OneTimeWorkRequestPeriodicWorkRequestの組み合わせ、ExpeditedWorkRequestによる即時実行、Constraintsによるネットワーク・充電条件の指定、これらを正しく使い分けることで、Doze・App Standby下でも実用的な実行頻度を保てます。Androidの大きな利点は、JobSchedulerやAlarmManagerに比べて「OSのバージョン差を吸収してくれる」点で、Android 8〜15まで同じコードで動かせるのは個人開発者には本当にありがたい設計です。

両者で決定的に違うのは、iOSはシステム主導でいつ実行するかを決め、開発者は「動くチャンス」を最大化する設計をするのに対し、Androidは開発者が条件と頻度をある程度コントロールできる点です。この前提を共有してから、コードに入ります。

iOS: BGTaskSchedulerの本番設定パターン

Rorkで生成されたInfo.plistには、デフォルトでバックグラウンドモードの設定が入っていないことが多いので、まずexpo-task-managerexpo-background-fetchを入れたうえで、app.jsonでバックグラウンドモードを宣言します。

// app.json
{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["fetch", "processing", "remote-notification"],
        "BGTaskSchedulerPermittedIdentifiers": [
          "net.rorklab.refresh",
          "net.rorklab.heavy-sync"
        ]
      }
    },
    "plugins": [
      ["expo-task-manager"],
      ["expo-background-fetch", { "ios": { "minimumInterval": 900 } }]
    ]
  }
}

注意点として、BGTaskSchedulerPermittedIdentifiersに登録した識別子と、コードでBGTaskScheduler.shared.registerに渡す識別子は完全一致させる必要があります。私は以前、net.rorklab.refreshnet.rorklab.app.refreshという揺らぎで2日間悩んだことがあるので、定数化してSwift側・JS側で共有することをおすすめします。同じことを繰り返さないために、識別子はconstants/background.tsのような専用ファイルにまとめるのが安全です。

JavaScript側では、expo-task-managerにタスクを定義してからBackgroundFetchに登録します。

// background-refresh.ts
import * as TaskManager from "expo-task-manager";
import * as BackgroundFetch from "expo-background-fetch";
import { fetchLatestFeed } from "./api/feed";
import { saveToCache } from "./store/cache";
 
export const REFRESH_TASK = "net.rorklab.refresh";
 
TaskManager.defineTask(REFRESH_TASK, async () => {
  const startedAt = Date.now();
  try {
    const feed = await fetchLatestFeed({ limit: 20, since: "auto" });
    await saveToCache("home_feed", feed);
    // 期待出力: console に "[refresh] 18 items in 1.2s" のような短い完了ログ
    console.log(`[refresh] ${feed.length} items in ${(Date.now() - startedAt) / 1000}s`);
    return BackgroundFetch.BackgroundFetchResult.NewData;
  } catch (error) {
    console.warn("[refresh] failed", error);
    return BackgroundFetch.BackgroundFetchResult.Failed;
  }
});
 
export async function registerBackgroundRefresh() {
  const status = await BackgroundFetch.getStatusAsync();
  if (status === BackgroundFetch.BackgroundFetchStatus.Restricted ||
      status === BackgroundFetch.BackgroundFetchStatus.Denied) {
    return;
  }
  await BackgroundFetch.registerTaskAsync(REFRESH_TASK, {
    minimumInterval: 60 * 15, // iOSはあくまで「最小間隔」のヒント
    stopOnTerminate: false,
    startOnBoot: true,
  });
}

ここでよくある間違いが2つあります。1つめはminimumIntervalを「実際の発火間隔」だと勘違いすることです。これはあくまでヒントで、iOSは端末の状態とユーザーの利用パターンから独自に判断します。実機では1日2〜4回、ユーザーが朝・昼・夜にアプリを開く習慣がある場合はその直前に集中して動く傾向があります。2つめは、戻り値を必ずNewData/NoData/Failedのいずれかで返すことです。返さないとシステムが「不誠実なアプリ」と判断し、徐々に発火頻度を絞ってきます。実装を急いでreturnを書き忘れていたために、リリース後の発火率が日に日に下がっていく事故を、私も一度経験しています。

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

この記事の続きを読む

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

この記事で得られること
Silent Pushが届かない問題で詰まっていた人が、APNSのthrottlingと本番運用パターンを理解して、実用的な配信設計に切り替えられます
iOSのBGTaskSchedulerとAndroidのWorkManagerを統一インターフェースで扱うReact Nativeブリッジ実装を、動くコードとともに手に入れられます
離脱しかけたユーザーの30%を取り戻す「バックグラウンド復帰ファネル」を、自分のRorkアプリに今日から組み込めるようになります
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

開発ツール2026-05-28
Rork アプリで iOS の BGTaskScheduler.submit が Error Code=1 で失敗するときの切り分け手順
Rork で作った iOS アプリで BGTaskScheduler.submit が Error Code=1 (Unavailable) を返してバックグラウンド更新が動かないときに、原因をひとつずつ潰していくための実用チェックリストです。
開発ツール2026-07-15
「、」が行頭に落ちた — Rork が組んだ名言カードと日本語の禁則処理
React Native の Text は日本語の行頭禁則を保証しません。実測した違反率、WORD JOINER による事前結合の実装、onTextLayout での自動監査、Rork Max(SwiftUI)との挙動差までまとめました。
開発ツール2026-07-10
React Compiler を Expo に入れて、手書き memo を 41 箇所消しました
Rork が生成した React Native 画面に React Compiler を導入し、再レンダリング回数を Profiler で実測しました。手書き memo/useCallback の削除判断、効かない箇所の見分け方、CI での回帰検知までをまとめています。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →