Network state
@react-native-community/netinfo's API on the Network framework and Android's ConnectivityManager, with change events sent to JavaScript.
- iOS: the Network framework's C functions. The path monitor's update handler is a Lucent function passed as a block, called on the main queue.
- Android:
WatcherextendsConnectivityManager.NetworkCallback, an SDK class, and overrides three of its methods. addEventListenersends each change to a JS callback and returns an id thatremoveEventListenertakes back.
import { PLATFORM } from "lucent:platform";import { nw_interface_type_t, nw_path_get_status, nw_path_monitor_cancel, nw_path_monitor_create, nw_path_monitor_set_queue, nw_path_monitor_set_update_handler, nw_path_monitor_start, nw_path_status_t, nw_path_uses_interface_type, type OS_nw_path, type OS_nw_path_monitor,} from "lucent:ios/Network";import { mainQueue } from "lucent:ios";import { ConnectivityManager, ConnectivityManager_NetworkCallback, Network, NetworkCapabilities,} from "lucent:android/android.net";import { appContext, available } from "lucent:android";
export interface NetInfoDetails { isConnectionExpensive: boolean;}
export interface NetInfoState { type: string; isConnected: boolean; isInternetReachable: boolean | null; details: NetInfoDetails | null;}
let nextId = 1;
// --- iOS: netinfo reads SCNetworkReachability (wifi unless the path is cellular) ---
function pathState(path: OS_nw_path): NetInfoState { if (nw_path_get_status(path) !== nw_path_status_t.nw_path_status_satisfied) { return { type: "none", isConnected: false, isInternetReachable: false, details: null }; } const cellular = nw_path_uses_interface_type( path, nw_interface_type_t.nw_interface_type_cellular, ); // Like SCNetworkReachability, which netinfo reads: only cellular is told apart. return { type: cellular ? "cellular" : "wifi", isConnected: true, isInternetReachable: null, details: { isConnectionExpensive: cellular }, };}
const monitors = new Map<number, OS_nw_path_monitor>();
function watchPath(onState: (state: NetInfoState) => void): number { const id = nextId++; const monitor = nw_path_monitor_create(); nw_path_monitor_set_update_handler(monitor, (path) => onState(pathState(path))); nw_path_monitor_set_queue(monitor, mainQueue()); monitors.set(id, monitor); nw_path_monitor_start(monitor); return id;}
function stopPath(id: number): void { const monitor = monitors.get(id); monitors.delete(id); if (monitor) nw_path_monitor_cancel(monitor);}
function fetchPath(): Promise<NetInfoState> { return new Promise((resolve) => { let id = 0; id = watchPath((state) => { stopPath(id); resolve(state); }); });}
// --- Android -----------------------------------------------------------------
function connectivity(): ConnectivityManager { const m = appContext().getSystemService(ConnectivityManager); if (!m) throw new Error("ConnectivityManager is not available"); return m;}
function capabilitiesState(capabilities: NetworkCapabilities | null): NetInfoState { if (!capabilities) return { type: "none", isConnected: false, isInternetReachable: false, details: null }; const has = (transport: number) => capabilities.hasTransport(transport); const type = has(NetworkCapabilities.TRANSPORT_BLUETOOTH) ? "bluetooth" : has(NetworkCapabilities.TRANSPORT_CELLULAR) ? "cellular" : has(NetworkCapabilities.TRANSPORT_ETHERNET) ? "ethernet" : has(NetworkCapabilities.TRANSPORT_WIFI) ? "wifi" : has(NetworkCapabilities.TRANSPORT_VPN) ? "vpn" : "unknown"; const suspended = available("android", 28) ? !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) : false; let reachable = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) && !suspended; if (has(NetworkCapabilities.TRANSPORT_VPN)) reachable = reachable && capabilities.getLinkDownstreamBandwidthKbps() !== 0; const isConnected = type !== "unknown"; return { type, isConnected, isInternetReachable: reachable, details: isConnected ? { isConnectionExpensive: connectivity().isActiveNetworkMetered() } : null, };}
function currentNetwork(): NetInfoState { const m = connectivity(); const network = m.getActiveNetwork(); return capabilitiesState(network ? m.getNetworkCapabilities(network) : null);}
/** The default network's changes, as netinfo follows them. */class Watcher extends ConnectivityManager_NetworkCallback { constructor(private readonly onState: (state: NetInfoState) => void) { super(); } onAvailable(_network: Network): void { this.onState(currentNetwork()); } onLost(_network: Network): void { this.onState(capabilitiesState(null)); } onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities): void { this.onState(capabilitiesState(capabilities)); }}
const watchers = new Map<number, Watcher>();
// --- The module --------------------------------------------------------------
export async function fetch(): Promise<NetInfoState> { return PLATFORM === "ios" ? fetchPath() : currentNetwork();}
/** Calls `listener` with the state now and on each change, until removeEventListener(id). */export async function addEventListener(listener: (state: NetInfoState) => void): Promise<number> { if (PLATFORM === "ios") { return watchPath(listener); } else { const id = nextId++; const watcher = new Watcher(listener); watchers.set(id, watcher); connectivity().registerDefaultNetworkCallback(watcher); listener(currentNetwork()); return id; }}
export async function removeEventListener(id: number): Promise<void> { if (PLATFORM === "ios") { stopPath(id); } else { const watcher = watchers.get(id); watchers.delete(id); if (watcher) connectivity().unregisterNetworkCallback(watcher); }}import * as NetInfo from "./src/netInfo.lucent";
const state = await NetInfo.fetch();console.log(state.type, state.isConnected);
const id = await NetInfo.addEventListener((next) => console.log(next.type));await NetInfo.removeEventListener(id);Source: netInfo.lucent.ts.