📶

Expo Networkを使ってwifiの接続を確認してみた

に公開

What Expo Network?

https://docs.expo.dev/versions/latest/sdk/network/#bluetooth
A library that provides access to the device's network such as its IP address, MAC address, and airplane mode status.

IPアドレス、MACアドレス、機内モードの状態など、デバイスのネットワークへのアクセスを提供するライブラリ。

Expoのプロジェクトを作成する。

bunx create-expo-app expo-wifi -t expo-template-blank-typescript

これを今回はbunで使ってみようと思います。

bun add expo-network

If you are installing this in an existing React Native app, start by installing expo in your project. Then, follow the additional instructions as mentioned by the library's README under "Installation in bare React Native projects" section.

Configuration

On Android, this module requires permissions to access the network and Wi-Fi state. The permissions ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE are added automatically.

既存のReact Nativeアプリにインストールする場合は、プロジェクトにexpoをインストールすることから始めます。その後、ライブラリのREADMEに記載されている「素のReact Nativeプロジェクトへのインストール」セクションの追加指示に従ってください。

構成

Androidでは、このモジュールはネットワークとWi-Fi状態にアクセスするためのパーミッションが必要です。パーミッションACCESS_NETWORK_STATEとACCESS_WIFI_STATEは自動的に追加されます。

自動的に追加か。。。
確かに設定はしてないな笑

example

完成品はこちら
このソースコードだけでwifiと通信できているか確認できるアプリ作れます。

全体のソースコード
App.tsx
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, RefreshControl } from 'react-native';
import { useEffect, useState, useCallback } from 'react';
import * as Network from 'expo-network';

export default function App() {
  const [networkState, setNetworkState] = useState<{
    isConnected: boolean;
    type: string | null;
    isInternetReachable: boolean;
    details: Network.NetworkState | null;
  }>({
    isConnected: false,
    type: null,
    isInternetReachable: false,
    details: null,
  });
  const [refreshing, setRefreshing] = useState(false);

  const getNetworkInfo = async () => {
    try {
      const networkState = await Network.getNetworkStateAsync();

      setNetworkState({
        isConnected: networkState?.isConnected ?? false,
        type: networkState?.type ?? null,
        isInternetReachable: networkState?.isInternetReachable ?? false,
        details: networkState ?? null,
      });
    } catch (error) {
      console.error('Error fetching network info:', error);
    }
  };

  const onRefresh = useCallback(async () => {
    setRefreshing(true);
    await getNetworkInfo();
    setRefreshing(false);
  }, []);

  useEffect(() => {
    getNetworkInfo();
  }, []);

  const renderNetworkStatus = () => {
    const statusColor = networkState.isConnected ? '#4CAF50' : '#F44336';
    return (
      <>
        <View style={styles.header}>
          <Text style={styles.headerText}>WiFi 接続状態</Text>
        </View>
        <View style={styles.card}>
          <View style={styles.statusContainer}>
            <View style={[styles.statusDot, { backgroundColor: statusColor }]} />
            <Text style={[styles.statusText, { color: statusColor }]}>
              {networkState.isConnected ? '接続中' : '未接続'}
            </Text>
          </View>
          <View style={styles.detailRow}>
            <Text style={styles.label}>接続タイプ:</Text>
            <Text style={styles.value}>
              {networkState.type === 'WIFI' ? 'WiFi'
                : networkState.type === 'CELLULAR' ? 'モバイル通信'
                  : networkState.type === 'BLUETOOTH' ? 'Bluetooth'
                    : networkState.type === 'ETHERNET' ? '有線LAN'
                      : networkState.type === 'VPN' ? 'VPN'
                        : networkState.type === 'OTHER' ? 'その他'
                          : '不明'}
            </Text>
          </View>
          <View style={styles.detailRow}>
            <Text style={styles.label}>インターネット:</Text>
            <Text style={styles.value}>
              {networkState.isInternetReachable ? '利用可能' : '利用不可'}
            </Text>
          </View>
          {!networkState.isConnected && (
            <View style={styles.errorMessage}>
              <Text style={styles.errorText}>
                WiFiに接続されていません。{'\n'}
                端末のWiFi設定を確認してください。
              </Text>
            </View>
          )}
        </View>
      </>
    );
  };

  return (
    <ScrollView
      style={styles.container}
      contentContainerStyle={styles.contentContainer}
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          title="更新中..."
          tintColor="#2196F3"
        />
      }
    >
      <StatusBar style="light" />
      {renderNetworkStatus()}
      <TouchableOpacity style={styles.refreshButton} onPress={onRefresh}>
        <Text style={styles.refreshButtonText}>接続状態を更新</Text>
      </TouchableOpacity>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  contentContainer: {
    padding: 16,
  },
  header: {
    backgroundColor: '#2196F3',
    padding: 16,
    paddingTop: 60,
    marginBottom: 16,
    marginHorizontal: -16,
  },
  headerText: {
    color: 'white',
    fontSize: 24,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  card: {
    backgroundColor: 'white',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  statusContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 16,
    justifyContent: 'center',
  },
  statusDot: {
    width: 12,
    height: 12,
    borderRadius: 6,
    marginRight: 8,
  },
  statusText: {
    fontSize: 24,
    fontWeight: 'bold',
  },
  detailRow: {
    flexDirection: 'row',
    marginBottom: 12,
    alignItems: 'center',
  },
  label: {
    fontSize: 16,
    color: '#666',
    width: 120,
  },
  value: {
    fontSize: 16,
    color: '#333',
    flex: 1,
  },
  errorMessage: {
    backgroundColor: '#FFF3F3',
    borderRadius: 8,
    padding: 16,
    marginTop: 16,
    borderWidth: 1,
    borderColor: '#FFE0E0',
  },
  errorText: {
    color: '#D32F2F',
    fontSize: 16,
    textAlign: 'center',
    lineHeight: 24,
  },
  refreshButton: {
    backgroundColor: '#2196F3',
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  refreshButtonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: '500',
  },
});

動作はこんな感じですね。
https://x.com/JBOY83062526/status/1867024974525108642

手順書も作ってみた参考までに

Expo WiFi Status App

このアプリケーションは、デバイスのWiFi接続状態をリアルタイムで確認できるExpoベースのモバイルアプリケーションです。

機能

  • WiFi接続状態のリアルタイム表示
  • ネットワークタイプの表示(WiFi、モバイル通信など)
  • インターネット接続状態の確認
  • プルトゥリフレッシュによる接続状態の更新

必要条件

環境構築

  1. bunのインストール(まだインストールしていない場合):
curl -fsSL https://bun.sh/install | bash
  1. プロジェクトのクローンとセットアップ:
# リポジトリをクローン
git clone [your-repository-url]
cd expo-wifi

# 依存関係のインストール
bun install
  1. アプリケーションの起動:
bun start

使用方法

  1. Expo Goアプリを起動
  2. QRコードをスキャン(またはURLを入力)してアプリを開く
  3. アプリが自動的にデバイスのWiFi接続状態を表示
  4. 画面を下にプルダウンして接続状態を更新
  5. 「接続状態を更新」ボタンをタップして手動更新も可能

技術仕様

  • Framework: Expo (SDK 52)
  • Language: TypeScript
  • 主要パッケージ:
    • expo-network: WiFiおよびネットワーク情報の取得
    • react-native: UIコンポーネント
    • expo-status-bar: ステータスバーの制御

WiFi通信の仕組み

このアプリケーションは以下のような方法でWiFi情報を取得しています:

  1. expo-networkパッケージを使用して、デバイスのネットワーク状態を取得
const networkState = await Network.getNetworkStateAsync();
  1. 取得できる情報:
    • 接続状態(接続中/未接続)
    • ネットワークタイプ(WiFi/モバイル通信など)
    • インターネット到達可能性

注意: Expoの制限により、WiFiの詳細な情報(SSID、信号強度など)や、
WiFiスキャン機能は利用できません。これらの機能が必要な場合は、
expo-dev-clientを使用するか、React Native CLIプロジェクトへの移行が必要です。

トラブルシューティング

  1. アプリが起動しない場合:

    • bunとExpo Goが最新バージョンかを確認
    • bun installを再実行
  2. ネットワーク情報が取得できない場合:

    • デバイスの位置情報設定が有効になっているか確認
    • アプリのネットワークアクセス権限を確認
  3. 更新が反映されない場合:

    • アプリを完全に終了して再起動
    • Expo Goをクリアして再起動

Discussion