メインコンテンツへスキップ

Capacitor アプリでネットワークステータスを検知する方法

Capacitor アプリでのネットワークステータス検知:オンライン/オフライン状態の取得、変更の監視、実際のインターネット接続確認、従量制ネットワークへの対応。

Capacitor モバイルアプリでオンライン・オフラインのネットワーク状態を検知する方法

Capacitor アプリでネットワーク状態を検知するには、以下をインストールしてください @capacitor/network、呼び出し Network.getStatus() 現在の状態を取得し、 networkStatusChange 更新を購読します。これにより、デバイスが接続されているか、Wi-Fi かモバイル通信かを判断できます。インターネットが実際に使えるか、接続が従量制かどうかを確認するには、reachability チェックを追加してください。 @capgo/capacitor-network-diagnostics本ガイドでは、Capacitor 8 code を用いて、これら3つのレイヤーすべてを解説します。

「ユーザーはオンラインか?」を判断する3つの問い

質問 API 備考
アクティブなネットワークインターフェースはありますか? Network.getStatus().connected 高速でイベント駆動型ですが、インターネット接続については何も示しません
どの種類のリンクですか? connectionType wifi, cellular, none, unknown 公式プラグインで
インターネットは実際に動作していますか? HTTPチェックまたは internetReachable リクエストまたはOS検証が必要
大容量のダウンロードは避けるべき? expensive, constrained 従量制ネットワークとiOSの低データモード

オフライン関連のバグの多くは、1行目を3行目として扱ってしまうことから起こります。ログインページ付きのホテルWi-Fiは接続済みと報告しますが、 connected: true すべての API コールがタイムアウトしてしまいます。

Network pluginをインストールする

bun add @capacitor/network
bunx cap sync

設定は不要です。Androidでは、このプラグインが ACCESS_NETWORK_STATE マージされたmanifestに追加します。インストール時に付与される通常の権限です。iOSでは何も必要ありません。

現在のステータスを読み取る

import { Network } from '@capacitor/network';

const status = await Network.getStatus();
console.log(status.connected);      // true or false
console.log(status.connectionType); // 'wifi' | 'cellular' | 'none' | 'unknown'

キャッシュから読み込むかネットワークから読み込むかを判断する前に、アプリ起動時にこれを呼び出してください。OSの状態を読み取るだけで通信は発生しないため、数ミリ秒で返されます。

ネットワークの変化を監視する

import { Network, type ConnectionStatus } from '@capacitor/network';
import type { PluginListenerHandle } from '@capacitor/core';

let handle: PluginListenerHandle | undefined;

export async function watchNetwork(onChange: (s: ConnectionStatus) => void) {
  onChange(await Network.getStatus());
  handle = await Network.addListener('networkStatusChange', onChange);
}

export async function stopWatchingNetwork() {
  await handle?.remove();
}

実用上の2つのポイント:

  • イベントをデバウンスする。 Wi-Fi からセルラーに切り替える際、両プラットフォームとも一時的に none 新しいリンクが確立する前に報告されることがあります。「オフライン」バナーを表示する前に 1〜2 秒のデバウンスを挟むことで、ちらつきを防げます。
  • リスナーの削除 コンポーネントがアンマウントされた際にリスナーを削除しないと、ホットリロードやナビゲーション後にコールバックが複数回実行されてしまいます。

React フック

import { useEffect, useState } from 'react';
import { Network, type ConnectionStatus } from '@capacitor/network';

export function useNetworkStatus() {
  const [status, setStatus] = useState<ConnectionStatus>({ connected: true, connectionType: 'unknown' });

  useEffect(() => {
    let timer: ReturnType<typeof setTimeout>;
    const listener = Network.addListener('networkStatusChange', (s) => {
      clearTimeout(timer);
      timer = setTimeout(() => setStatus(s), s.connected ? 0 : 1500);
    });
    Network.getStatus().then(setStatus);
    return () => {
      clearTimeout(timer);
      listener.then((l) => l.remove());
    };
  }, []);

  return status;
}

オンラインへの切り替えは即座に反映され、オフラインへの切り替えは1.5秒待ちます。同じパターンは Vue の composable や Angular の service でも機能し、 BehaviorSubject.

「接続済み」がオンラインを意味しない理由

connected: true は OS のネットワークスタックから取得されます。次の場合でも true のままです:

  • Wi-Fi ネットワークにキャプティブポータルがある場合(空港、ホテル、列車など)
  • ルーターに上流接続がない場合
  • DNSが壊れている、またはフィルタリングされている
  • 企業のファイアウォールやVPNが API ドメインをブロックしている
  • 自社バックエンドがダウンしている

解決策は、自分で管理している対象への到達性チェックです:

export async function canReachApi(timeoutMs = 4000): Promise<boolean> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const res = await fetch('https://api.example.com/health', {
      method: 'HEAD',
      cache: 'no-store',
      signal: controller.signal,
    });
    return res.ok;
  } catch {
    return false;
  } finally {
    clearTimeout(timer);
  }
}

アプリの起動時、 networkStatusChange レポートによると、 connected: trueリクエストが失敗した後にも実行してください。数秒おきのポーリングは避けてください。バッテリーとデータ通信を消耗します。ヘルスエンドポイントがCORSでアプリのオリジンを許可していることを確認してください(capacitor://localhost iOSでは適用され、 https://localhost Androidではデフォルトで有効)、または CapacitorHttp を使用してネイティブの code から送信します。

ネットワーク診断でより詳細なステータスを取得

@capgo/capacitor-network-diagnostics OSが把握している情報をより多く読み取り、WebViewのCORSをバイパスするネイティブチェックを実行できます:

bun add @capgo/capacitor-network-diagnostics
bunx cap sync
import { NetworkDiagnostics } from '@capgo/capacitor-network-diagnostics';

const status = await NetworkDiagnostics.getNetworkStatus();
// {
//   connected: true,
//   connectionType: 'wifi' | 'cellular' | 'ethernet' | 'vpn' | 'other' | 'unknown' | 'none',
//   internetReachable: true,
//   expensive: false,      // metered / cellular
//   constrained: false,    // iOS Low Data Mode
//   captivePortal: false,  // Android only
//   details: { ... }
// }

フィールドとOSの対応関係:

項目 Androidでは iOSと
internetReachable NET_CAPABILITY_VALIDATED (Androidがインターネットを確認) ミラーリング: NWPathMonitor パスのステータス
expensive isActiveNetworkMetered() NWPath.isExpensive
constrained 常に false NWPath.isConstrained (低データモード)
captivePortal NET_CAPABILITY_CAPTIVE_PORTAL 未報告
vpn 型 はい はい

Androidの場合、 internetReachable は強力なシグナルです。OSがすでに接続をテスト済みのため、iOSではより近い状態を示します。 connectedなので、重要なフローではHTTPチェックを維持してください。

ネイティブの Reachability チェック

const api = await NetworkDiagnostics.testUrl({
  url: 'https://api.example.com/health',
  method: 'HEAD',
  timeoutMs: 4000,
});
// api.reachable, api.statusCode, api.durationMs, api.errorCode

const socket = await NetworkDiagnostics.testWebSocket({ url: 'wss://realtime.example.com' });
const port = await NetworkDiagnostics.testPort({ host: 'mqtt.example.com', port: 8883 });

ネイティブリクエストはCORSの対象外なので、サードパーティのホストもテストできます。

サポート用の「ネットワークレポート」画面

ユーザーが「アプリが動かない」と言ったとき、ワンタップの診断レポートが何時間ものやり取りを省きます。

const report = await NetworkDiagnostics.runDiagnostics({
  urls: [{ url: 'https://api.example.com/health' }, { url: 'https://cdn.example.com/ping' }],
  websockets: [{ url: 'wss://realtime.example.com' }],
  download: { url: 'https://cdn.example.com/1mb.bin', maxBytes: 1_048_576 },
  packetLoss: { url: 'https://api.example.com/health', count: 5 },
});

console.log(report.issues);        // human-readable list of problems
console.log(report.download?.mbps);
console.log(report.packetLoss?.lossPercent);

JSONをサポートチケットに添付するだけで、問題がDNSなのか、WebSocketのブロックなのか、キャプティブポータルなのか、回線が遅いのかがすぐにわかります。

従量制通信と低データモードへの対応

データ通信量を抑えることは、モバイルデータが高い市場でレビューを伸ばす近道です。

export async function shouldPrefetchMedia() {
  const s = await NetworkDiagnostics.getNetworkStatus();
  if (!s.connected) return false;
  if (s.constrained) return false; // user asked iOS to save data
  if (s.expensive) return false;   // cellular or metered Wi-Fi
  return true;
}

動画の自動再生、画像の画質、バックグラウンド同期、大容量ダウンロードの判断に活用しましょう。AndroidではWi-Fiを従量制として設定できるため、Wi-Fiなら無料とは限らない点に注意が必要です。

オフラインバナーの実装

リクエストの失敗を見せるより、小さなバナーを出す方が分かりやすいです。上記のフックを使うと:

export function OfflineBanner() {
  const { connected } = useNetworkStatus();
  if (connected) return null;
  return (
    <div role="status" aria-live="polite" className="offline-banner">
      You are offline. Changes will sync when you reconnect.
    </div>
  );
}

role="status" を使用して aria-live="polite" スクリーンリーダーが変更を通知できるようになります。フレームワーク別のUIについては、こちらのガイドを参照してください: Vue、Angular、Reactでオフライン画面を構築する方法.

オフライン中は処理をキューに積む

検知はオフライン対応の半分に過ぎません。ステータスがオフラインになったら:

  1. 読み取りはローカルストレージから行います。たとえば、次のようなSQLiteデータベースは @capgo/capacitor-fast-sql 大規模なデータセットを〜よりもうまく扱えます localStorage.
  2. ユーザー操作は、タイムスタンプと冪等キーを付けてアウトボックステーブルに書き込みます。
  3. オンラインに networkStatusChange 復帰したら、次を実行します: canReachApi()、その後アウトボックスを順番にフラッシュします。
  4. 失敗したリクエストは、数分を上限とする指数バックオフでリトライします。

アプリを閉じても完了させる必要があるアップロードは、Webフォールバックではなくネイティブのアップローダーに渡してください。 fetch.

Webフォールバック

@capacitor/network ブラウザでも動作し、基になっているのは navigator.onLine および online と offline イベントです。つまり、code はその環境で動作し、 bun run dev PWAでも変更なしで動作しますが、 connectionType 多くの場合、 unknown Web上になります。Chromeの navigator.connection 公開し effectiveType と saveData、Safari は対応していないため、任意のヒントとして扱ってください。

トラブルシューティング

iOS シミュレーターではリスナーが発火しません:シミュレーターは Mac のネットワークを共有します。Mac の Wi-Fi を切り替えるか、機内モードを有効にしたデバイスでテストしてください。

connectionType が unknown VPN 接続時: 公式プラグインは次のものだけを報告します wifi, cellular, none と unknown. 次を返す diagnostics プラグインを使用してください vpn と ethernet.

ネットワークを切り替えると、アプリが1秒間オフライン表示になる: 上記のように、オフラインへの遷移にデバウンスを適用してください。

ヘルスチェックが実機では失敗するが、ブラウザでは成功する: CORS。許可する capacitor://localhost と https://localhost, または NetworkDiagnostics.testUrl, ネイティブで動作します。

navigator.onLine プラグインがオフラインと判定している間は true ですプラグインを信頼してください。一部の Android WebView は navigator.onLine 確実に更新されません。

オフライン修正をOTAで配信

オフライン処理は主に JavaScript で行います: しきい値、バナー、リトライロジック、キャッシュルール。 Capgo Live Updatesを使えば、それらをその日のうちに全ユーザー向けに調整できます。Capgo はバックグラウンドで更新バンドルをダウンロードし、失敗時は現在のバージョンを維持するので、接続が悪くても中途半端な更新状態にはなりません。 ネットワーク診断のドキュメント すべてのオプションを一覧表示します。

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。