react-native-local-network-info 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +158 -0
- package/android/build.gradle +20 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/localnetworkinfo/LocalNetworkInfoModule.kt +371 -0
- package/build/LocalNetworkInfo.types.d.ts +97 -0
- package/build/LocalNetworkInfo.types.d.ts.map +1 -0
- package/build/LocalNetworkInfo.types.js +2 -0
- package/build/LocalNetworkInfo.types.js.map +1 -0
- package/build/LocalNetworkInfoModule.d.ts +11 -0
- package/build/LocalNetworkInfoModule.d.ts.map +1 -0
- package/build/LocalNetworkInfoModule.js +4 -0
- package/build/LocalNetworkInfoModule.js.map +1 -0
- package/build/LocalNetworkInfoModule.web.d.ts +14 -0
- package/build/LocalNetworkInfoModule.web.d.ts.map +1 -0
- package/build/LocalNetworkInfoModule.web.js +27 -0
- package/build/LocalNetworkInfoModule.web.js.map +1 -0
- package/build/index.d.ts +43 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +48 -0
- package/build/index.js.map +1 -0
- package/build/useLocalIp.d.ts +18 -0
- package/build/useLocalIp.d.ts.map +1 -0
- package/build/useLocalIp.js +43 -0
- package/build/useLocalIp.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/LocalNetworkInfo.podspec +29 -0
- package/ios/LocalNetworkInfoModule.swift +241 -0
- package/package.json +61 -0
- package/src/LocalNetworkInfo.types.ts +110 -0
- package/src/LocalNetworkInfoModule.ts +17 -0
- package/src/LocalNetworkInfoModule.web.ts +35 -0
- package/src/index.ts +57 -0
- package/src/useLocalIp.ts +50 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which network role is currently providing the device's local IPv4.
|
|
3
|
+
*
|
|
4
|
+
* - `wifi` — the device is connected to a WiFi network as a station/client.
|
|
5
|
+
* - `hotspot` — the device is acting as a WiFi hotspot (SoftAP) host.
|
|
6
|
+
* - `none` — neither WiFi nor hotspot is providing a usable LAN address
|
|
7
|
+
* (e.g. cellular-only, airplane mode, or offline).
|
|
8
|
+
*
|
|
9
|
+
* When both WiFi and hotspot are active simultaneously, `wifi` takes precedence.
|
|
10
|
+
*/
|
|
11
|
+
export type NetworkRole = 'wifi' | 'hotspot' | 'none';
|
|
12
|
+
|
|
13
|
+
/** The platform that produced a given snapshot. */
|
|
14
|
+
export type DevicePlatform = 'ios' | 'android' | 'web';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The usable host range of the hotspot subnet — i.e. the addresses a client
|
|
18
|
+
* could be assigned. This is the whole subnet's usable span (network+1 ..
|
|
19
|
+
* broadcast-1), so the host/gateway itself is one address *within* this range
|
|
20
|
+
* (it is not a boundary). When scanning for clients, skip your own `ip`.
|
|
21
|
+
*
|
|
22
|
+
* Example: host/gateway `10.121.184.216` on a `/24` → range `10.121.184.1` ..
|
|
23
|
+
* `10.121.184.254` (a client may legitimately be `.215` *or* `.217`).
|
|
24
|
+
*/
|
|
25
|
+
export interface ClientIpRange {
|
|
26
|
+
/** First usable client IPv4 in the subnet. */
|
|
27
|
+
first: string;
|
|
28
|
+
/** Last usable client IPv4 in the subnet. */
|
|
29
|
+
last: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A point-in-time snapshot of the device's local network position.
|
|
34
|
+
*
|
|
35
|
+
* The same shape is returned by {@link getLocalIp} and delivered to
|
|
36
|
+
* {@link addNetworkChangeListener} / {@link useLocalIp}.
|
|
37
|
+
*/
|
|
38
|
+
export interface LocalIpInfo {
|
|
39
|
+
/**
|
|
40
|
+
* The device's own local IPv4 on the active LAN interface, or `null` when
|
|
41
|
+
* there is no WiFi/hotspot LAN address. Follows the precedence:
|
|
42
|
+
* WiFi station IP first, then hotspot host IP.
|
|
43
|
+
*/
|
|
44
|
+
ip: string | null;
|
|
45
|
+
|
|
46
|
+
/** Which role is providing {@link ip}. WiFi takes precedence over hotspot. */
|
|
47
|
+
role: NetworkRole;
|
|
48
|
+
|
|
49
|
+
/** `true` when connected to a WiFi network as a client/station. */
|
|
50
|
+
isWifiConnected: boolean;
|
|
51
|
+
|
|
52
|
+
/** `true` when this device is currently acting as a WiFi hotspot (SoftAP) host. */
|
|
53
|
+
isHotspotHost: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Name of the network interface backing {@link ip}
|
|
57
|
+
* (e.g. `en0`, `wlan0`, `bridge100`, `ap0`), or `null`.
|
|
58
|
+
*/
|
|
59
|
+
interfaceName: string | null;
|
|
60
|
+
|
|
61
|
+
/** Dotted IPv4 subnet mask for {@link ip} (e.g. `255.255.255.0`), or `null`. */
|
|
62
|
+
netmask: string | null;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The LAN gateway.
|
|
66
|
+
*
|
|
67
|
+
* - As a **hotspot host**, this is the device itself (it *is* the gateway).
|
|
68
|
+
* - As an **Android station**, this is the real default-route gateway
|
|
69
|
+
* (read from `LinkProperties`).
|
|
70
|
+
* - As an **iOS station**, this is a best-effort guess derived from the
|
|
71
|
+
* subnet (the first host address), because Apple exposes no public gateway
|
|
72
|
+
* API. Treat the iOS station gateway as a heuristic.
|
|
73
|
+
*/
|
|
74
|
+
gateway: string | null;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* When this device is a hotspot host, the predicted inclusive range of IPs
|
|
78
|
+
* its clients can receive, derived from the host IP + subnet:
|
|
79
|
+
*
|
|
80
|
+
* - **iOS** — the fixed Personal Hotspot range `172.20.10.2`–`172.20.10.14`.
|
|
81
|
+
* - **Android** — computed from the (randomized on Android 11+) SoftAP subnet.
|
|
82
|
+
*
|
|
83
|
+
* `null` when the device is not a hotspot host.
|
|
84
|
+
*/
|
|
85
|
+
predictedClientRange: ClientIpRange | null;
|
|
86
|
+
|
|
87
|
+
/** Platform that captured this snapshot. */
|
|
88
|
+
platform: DevicePlatform;
|
|
89
|
+
|
|
90
|
+
/** Epoch milliseconds when this snapshot was captured natively. */
|
|
91
|
+
timestamp: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Per-interface detail, returned by {@link getAllInterfaces} for debugging. */
|
|
95
|
+
export interface NetworkInterfaceInfo {
|
|
96
|
+
/** Interface name (e.g. `en0`, `wlan0`, `bridge100`, `ap0`, `pdp_ip0`). */
|
|
97
|
+
name: string;
|
|
98
|
+
/** The interface's IPv4 address. */
|
|
99
|
+
ip: string;
|
|
100
|
+
/** Dotted IPv4 subnet mask, when known. */
|
|
101
|
+
netmask: string | null;
|
|
102
|
+
/** Best-effort role classification of the interface. */
|
|
103
|
+
role: 'wifi' | 'hotspot' | 'cellular' | 'ethernet' | 'other';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Native event map for the module. */
|
|
107
|
+
export type LocalNetworkInfoModuleEvents = {
|
|
108
|
+
/** Fired whenever the device's network position changes. */
|
|
109
|
+
onNetworkChange: (info: LocalIpInfo) => void;
|
|
110
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NativeModule, requireNativeModule } from 'expo';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
LocalNetworkInfoModuleEvents,
|
|
5
|
+
LocalIpInfo,
|
|
6
|
+
NetworkInterfaceInfo,
|
|
7
|
+
} from './LocalNetworkInfo.types';
|
|
8
|
+
|
|
9
|
+
declare class LocalNetworkInfoModule extends NativeModule<LocalNetworkInfoModuleEvents> {
|
|
10
|
+
/** Capture the current local-IP/network-role snapshot. */
|
|
11
|
+
getLocalIpAsync(): Promise<LocalIpInfo>;
|
|
12
|
+
/** Enumerate all up, non-loopback IPv4 interfaces (for debugging/inspection). */
|
|
13
|
+
getAllInterfacesAsync(): Promise<NetworkInterfaceInfo[]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// The string must match `Name("LocalNetworkInfo")` in the Swift/Kotlin modules.
|
|
17
|
+
export default requireNativeModule<LocalNetworkInfoModule>('LocalNetworkInfo');
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { registerWebModule, NativeModule } from 'expo';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
LocalNetworkInfoModuleEvents,
|
|
5
|
+
LocalIpInfo,
|
|
6
|
+
NetworkInterfaceInfo,
|
|
7
|
+
} from './LocalNetworkInfo.types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Web fallback. Browsers do not expose the device's local LAN IPv4, so this
|
|
11
|
+
* reports `role: 'none'` with a `null` IP. It exists so the package imports
|
|
12
|
+
* cleanly on web (Metro resolves `*.web.ts`) and the API surface stays stable.
|
|
13
|
+
*/
|
|
14
|
+
class LocalNetworkInfoModule extends NativeModule<LocalNetworkInfoModuleEvents> {
|
|
15
|
+
async getLocalIpAsync(): Promise<LocalIpInfo> {
|
|
16
|
+
return {
|
|
17
|
+
ip: null,
|
|
18
|
+
role: 'none',
|
|
19
|
+
isWifiConnected: false,
|
|
20
|
+
isHotspotHost: false,
|
|
21
|
+
interfaceName: null,
|
|
22
|
+
netmask: null,
|
|
23
|
+
gateway: null,
|
|
24
|
+
predictedClientRange: null,
|
|
25
|
+
platform: 'web',
|
|
26
|
+
timestamp: Date.now(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getAllInterfacesAsync(): Promise<NetworkInterfaceInfo[]> {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default registerWebModule(LocalNetworkInfoModule, 'LocalNetworkInfoModule');
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
import LocalNetworkInfoModule from './LocalNetworkInfoModule';
|
|
4
|
+
import { LocalIpInfo, NetworkInterfaceInfo } from './LocalNetworkInfo.types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Capture the device's current local-IP / network-role snapshot.
|
|
8
|
+
*
|
|
9
|
+
* Resolves with WiFi-station info when connected to WiFi; otherwise hotspot-host
|
|
10
|
+
* info when the device is sharing its connection; otherwise `role: 'none'`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const info = await getLocalIp();
|
|
15
|
+
* if (info.role === 'wifi') console.log('On WiFi at', info.ip);
|
|
16
|
+
* if (info.role === 'hotspot') console.log('Hotspot host at', info.ip);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export async function getLocalIp(): Promise<LocalIpInfo> {
|
|
20
|
+
return LocalNetworkInfoModule.getLocalIpAsync();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enumerate every up, non-loopback IPv4 interface with a best-effort role
|
|
25
|
+
* classification. Useful for debugging odd devices/vendors.
|
|
26
|
+
*/
|
|
27
|
+
export async function getAllInterfaces(): Promise<NetworkInterfaceInfo[]> {
|
|
28
|
+
return LocalNetworkInfoModule.getAllInterfacesAsync();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe to network changes. The listener fires with a fresh
|
|
33
|
+
* {@link LocalIpInfo} whenever connectivity, WiFi, or hotspot state changes.
|
|
34
|
+
*
|
|
35
|
+
* Remember to call `.remove()` on the returned subscription to avoid leaks
|
|
36
|
+
* (or use {@link useLocalIp}, which manages this for you).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const sub = addNetworkChangeListener((info) => {
|
|
41
|
+
* console.log('network changed →', info.role, info.ip);
|
|
42
|
+
* });
|
|
43
|
+
* // later
|
|
44
|
+
* sub.remove();
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function addNetworkChangeListener(
|
|
48
|
+
listener: (info: LocalIpInfo) => void
|
|
49
|
+
): EventSubscription {
|
|
50
|
+
return LocalNetworkInfoModule.addListener('onNetworkChange', listener);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The raw native module instance (advanced use). */
|
|
54
|
+
export { default as LocalNetworkInfoModule } from './LocalNetworkInfoModule';
|
|
55
|
+
|
|
56
|
+
export { useLocalIp } from './useLocalIp';
|
|
57
|
+
export * from './LocalNetworkInfo.types';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import LocalNetworkInfoModule from './LocalNetworkInfoModule';
|
|
4
|
+
import { LocalIpInfo } from './LocalNetworkInfo.types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React hook that returns the latest {@link LocalIpInfo} and keeps it updated as
|
|
8
|
+
* the network changes.
|
|
9
|
+
*
|
|
10
|
+
* It captures an initial snapshot on mount and then subscribes to native
|
|
11
|
+
* `onNetworkChange` events, cleaning up the subscription on unmount.
|
|
12
|
+
*
|
|
13
|
+
* @returns the latest snapshot, or `null` until the first snapshot resolves.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const info = useLocalIp();
|
|
18
|
+
* return <Text>{info?.ip ?? 'detecting…'} ({info?.role})</Text>;
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function useLocalIp(): LocalIpInfo | null {
|
|
22
|
+
const [info, setInfo] = useState<LocalIpInfo | null>(null);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let mounted = true;
|
|
26
|
+
|
|
27
|
+
LocalNetworkInfoModule.getLocalIpAsync()
|
|
28
|
+
.then((snapshot) => {
|
|
29
|
+
if (mounted) {
|
|
30
|
+
setInfo(snapshot);
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
.catch(() => {
|
|
34
|
+
// Swallow — a change event or a later manual refresh will populate it.
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const subscription = LocalNetworkInfoModule.addListener('onNetworkChange', (snapshot) => {
|
|
38
|
+
if (mounted) {
|
|
39
|
+
setInfo(snapshot);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
mounted = false;
|
|
45
|
+
subscription.remove();
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
return info;
|
|
50
|
+
}
|