Network
@symbiote-native/network wraps expo-network
so every SymbioteNative adapter can read the device’s network connection state. Like
battery, it’s built on expo-modules-core and mixes stateless one-shot
calls with exactly one live subscription: useNetworkState/NetworkStateService.connect() seed from
a one-shot getNetworkStateAsync() call and then subscribe to addNetworkStateListener for live
updates, the same seed-then-subscribe shape as battery’s own hooks — everything else
(getIpAddressAsync, isAirplaneModeEnabledAsync) is a plain stateless function, same as
local-auth.
| OS platform | Support |
|---|---|
| iOS | ✅ live |
| Android | ✅ live |
| Framework adapter | Support |
|---|---|
| React | ✅ live |
| Vue | ✅ live |
| Angular | ✅ live |
| Svelte | ✅ live |
Installation
Section titled “Installation”npm install @symbiote-native/networkexpo-network and expo-modules-core come along as regular dependencies, pinned to exact
versions — never install either yourself, and never add the expo meta-package to your project
(it bundles its own Metro/Babel pipeline, which conflicts with this project’s own).
Live network state
Section titled “Live network state”import { Text } from '@symbiote-native/react';import { useNetworkState } from '@symbiote-native/network/react';
export default function ConnectionStatus() { const networkState = useNetworkState(); // NetworkState, {} until the first reading arrives
return <Text>{networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline'}</Text>;}<script setup lang="ts">import { useNetworkState } from '@symbiote-native/network/vue';
const networkState = useNetworkState(); // Ref<NetworkState>, {} until the first reading arrives</script>
<template> <Text>{{ networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline' }}</Text></template>import { Component, inject } from '@angular/core';import { Text } from '@symbiote-native/angular';import { NetworkStateService } from '@symbiote-native/network/angular';
@Component({ standalone: true, imports: [Text], template: `<Text>{{ networkState().isConnected ? 'Connected via ' + networkState().type : 'Offline' }}</Text>`,})export class ConnectionStatus { readonly networkState = inject(NetworkStateService).connect();}<script lang="ts"> import { Text } from '@symbiote-native/svelte'; import { useNetworkState } from '@symbiote-native/network/svelte';
const networkState = useNetworkState(); // { readonly current: NetworkState }, {} until the first reading arrives</script>
<Text>{networkState.current.isConnected ? `Connected via ${networkState.current.type}` : 'Offline'}</Text>One-shot functions
Section titled “One-shot functions”The stateless functions are already framework-agnostic — import them straight from the package root, on any adapter:
import { getIpAddressAsync, isAirplaneModeEnabledAsync } from '@symbiote-native/network';
const ipAddress = await getIpAddressAsync(); // "0.0.0.0" if it could not be retrievedconst airplaneMode = await isAirplaneModeEnabledAsync();Functions
Section titled “Functions”| Signature | Description |
|---|---|
getNetworkStateAsync(): Promise<NetworkState> |
Gets the device’s current network connection state — { type, isConnected, isInternetReachable } |
getIpAddressAsync(): Promise<string> |
Gets the device’s current IPv4 address. Resolves "0.0.0.0" if it could not be retrieved |
isAirplaneModeEnabledAsync(): Promise<boolean> |
Tells if the device is in airplane mode |
addNetworkStateListener(listener): EventSubscription |
Subscribes to network-state-change events (connection type, connected, internet reachable); call .remove() on the returned subscription to unsubscribe. The primitive useNetworkState/NetworkStateService.connect() wrap |
useNetworkState() / NetworkStateService.connect()
Section titled “useNetworkState() / NetworkStateService.connect()”React (/react) |
Vue (/vue) |
Angular (/angular) |
Svelte (/svelte) |
Signature | Returns |
|---|---|---|---|---|---|
useNetworkState |
useNetworkState |
NetworkStateService.connect() |
useNetworkState |
() |
Live NetworkState — plain value (React), Ref<NetworkState> (Vue), Signal<NetworkState> (Angular), { readonly current: NetworkState } (Svelte, read as .current), seeded {} |
NetworkState / NetworkStateEvent
Section titled “NetworkState / NetworkStateEvent”| Field | Type | Description |
|---|---|---|
type |
NetworkStateType | undefined |
The current network connection type |
isConnected |
boolean | undefined |
Whether there is an active network connection — does not mean internet is reachable. false when type is NONE/UNKNOWN, true otherwise |
isInternetReachable |
boolean | undefined |
Whether the internet is reachable over the current connection — see the caveat above for the Android-vs-iOS check difference |
NetworkStateEvent is a plain alias of NetworkState, passed as the argument to
addNetworkStateListener’s listener.
NetworkStateType
Section titled “NetworkStateType”| Member | Value | Description |
|---|---|---|
NONE |
'NONE' |
No active network connection detected |
UNKNOWN |
'UNKNOWN' |
The connection type could not be determined |
CELLULAR |
'CELLULAR' |
Active connection over mobile data |
WIFI |
'WIFI' |
Active connection over Wi-Fi |
BLUETOOTH |
'BLUETOOTH' |
Active connection over Bluetooth. @platform android |
ETHERNET |
'ETHERNET' |
Active connection over Ethernet |
WIMAX |
'WIMAX' |
Active connection over WiMAX. @platform android |
VPN |
'VPN' |
Active connection over VPN. @platform android |
OTHER |
'OTHER' |
Active connection over any other type. @platform android |
isAirplaneModeEnabledAsyncthrows on iOS rather than resolvingfalse. iOS’s native module defines onlygetIpAddressAsyncandgetNetworkStateAsync, so the presence check in the core raises anUnavailabilityErrorbefore anything is called. Branch onPlatform.OS === 'android'at a cross-platform call site.getIpAddressAsyncreturns a Wi-Fi address on both platforms. iOS walks the interface list and keeps only interfaces nameden*; Android readsWifiManager’s connection info. On a cellular-only connection both fall through to"0.0.0.0"— that value means “no Wi-Fi address”, not “the call failed”.- A one-shot
getNetworkStateAsync()on iOS can block for up to five seconds and then look offline. The native module starts a throwawayNWPathMonitorfor the call and waits on a semaphore; if no path update arrives in time it returns the same{ type: 'NONE', isConnected: false, isInternetReachable: false }a genuinely disconnected device returns. The event path (addNetworkStateListener, and souseNetworkState) does not pay this cost — it reads the module’s own long-lived monitor. - Android state events are debounced by 250 ms.
ConnectivityManagercallbacks collapse onto a single delayed emission, so a burst of transitions arrives as one event and the settled state lands a quarter-second late. Disconnects are the exception — they are emitted immediately, to avoid re-reading a just-lost network as still connected.
How the wrapper works
Section titled “How the wrapper works”@symbiote-native/network ships zero React/Vue/Angular logic in expo-network itself — that
package’s own JS is hand-ported, verbatim, into this package’s own core/ (its types file has no
expo meta-package import to swap out in the first place, unlike local-auth’s or brightness’s):
packages/network/src/├── core/ getNetworkStateAsync/getIpAddressAsync/isAirplaneModeEnabledAsync +│ addNetworkStateListener; native-module.ts resolves the single│ `ExpoNetwork` native module via expo-modules-core's│ requireNativeModule├── react/hooks/use-network-state @symbiote-native/network/react├── vue/composables/use-network-state @symbiote-native/network/vue├── svelte/runes/use-network-state @symbiote-native/network/svelte└── angular/services/network-state.service @symbiote-native/network/angularEach adapter’s useNetworkState/NetworkStateService is a thin lifecycle wrapper — seed from the
one-shot call, subscribe on mount, unsubscribe on unmount — over the same core functions, the
same one-listener seed-then-subscribe shape as
battery’s three hooks. The native code itself is
never vendored or copied — expo-modules-autolinking resolves it straight out of node_modules
(see the native setup guide).