Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEPRECATION: Make NetInfo API cross platform and expose whether connection is 2g/3g/4g #14618

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 107 additions & 41 deletions Libraries/Network/NetInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const NetInfoEventEmitter = new NativeEventEmitter(RCTNetInfo);
const DEVICE_CONNECTIVITY_EVENT = 'networkStatusDidChange';

type ChangeEventName = $Enum<{
connectionchange: string,
change: string,
}>;

Expand Down Expand Up @@ -79,56 +80,51 @@ const _isConnectedSubscriptions = new Map();
* NetInfo exposes info about online/offline status
*
* ```
* NetInfo.fetch().then((reach) => {
* console.log('Initial: ' + reach);
* NetInfo.getConnection().then((connection) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is quite confusing (because it is not connection), can/should we rename it to something like getConnectionInfo or getConnectionStatus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shergin I modeled it after the W3C Network Information API which offers this information off of a connection property (https://wicg.github.io/netinfo/#-dfn-connection-dfn-attribute). I can switch it to getConnectionInfo if you'd prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. The connection attribute, when getting, returns an object that implements the NetworkInformation interface. Yeah, I think using getConnectionInfo or getConnectionInformation would be much better.

* console.log('Initial, type: ' + connection.type + ', effectiveType: ' + connection.effectiveType);
* });
* function handleFirstConnectivityChange(reach) {
* console.log('First change: ' + reach);
* function handleFirstConnectivityChange(connection) {
* console.log('First change, type: ' + connection.type + ', effectiveType: ' + connection.effectiveType);
* NetInfo.removeEventListener(
* 'change',
* 'connectionchange',
* handleFirstConnectivityChange
* );
* }
* NetInfo.addEventListener(
* 'change',
* 'connectionchange',
* handleFirstConnectivityChange
* );
* ```
*
* ### IOS
*
* Asynchronously determine if the device is online and on a cellular network.
*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* ### ConnectionType enum

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* `ConnectionType` describes the type of connection the device is using to communicate with the network.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* Cross platform values for `ConnectionType`:
* - `none` - device is offline
* - `wifi` - device is online and connected via wifi, or is the iOS simulator
* - `cell` - device is connected via Edge, 3G, WiMax, or LTE
* - `cellular` - device is connected via Edge, 3G, WiMax, or LTE
* - `unknown` - error case and the network status is unknown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* Android-only values for `ConnectionType`:
* - `bluetooth` - device is connected via Bluetooth
* - `ethernet` - device is connected via Ethernet
* - `wimax` - device is connected via WiMAX
*
* ### EffectiveConnectionType enum

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* Cross platform values for `EffectiveConnectionType`:
* - `2g`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* - `3g`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* - `4g`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* - `unknown`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

*
* ### Android
*
* To request network info, you need to add the following line to your
* app's `AndroidManifest.xml`:
*
* `<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />`
* Asynchronously determine if the device is connected and details about that connection.
*
* Android Connectivity Types.
*
* - `NONE` - device is offline
* - `BLUETOOTH` - The Bluetooth data connection.
* - `DUMMY` - Dummy data connection.
* - `ETHERNET` - The Ethernet data connection.
* - `MOBILE` - The Mobile data connection.
* - `MOBILE_DUN` - A DUN-specific Mobile data connection.
* - `MOBILE_HIPRI` - A High Priority Mobile data connection.
* - `MOBILE_MMS` - An MMS-specific Mobile data connection.
* - `MOBILE_SUPL` - A SUPL-specific Mobile data connection.
* - `VPN` - A virtual network using one or more native bearers. Requires API Level 21
* - `WIFI` - The WIFI data connection.
* - `WIMAX` - The WiMAX data connection.
* - `UNKNOWN` - Unknown data connection.
*
* The rest ConnectivityStates are hidden by the Android API, but can be used if necessary.
*
* ### isConnectionExpensive
*
Expand Down Expand Up @@ -167,22 +163,77 @@ const _isConnectedSubscriptions = new Map();
* handleFirstConnectivityChange
* );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* ```
*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* ### Connectivity Types (deprecated)
*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* The following connectivity types are deprecated. They're used by the deprecated APIs `fetch` and the `change` event.
*
* iOS connectivity types (deprecated):
* - `none` - device is offline
* - `wifi` - device is online and connected via wifi, or is the iOS simulator
* - `cell` - device is connected via Edge, 3G, WiMax, or LTE

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* - `unknown` - error case and the network status is unknown
*
* Android connectivity types (deprecated).
* - `NONE` - device is offline
* - `BLUETOOTH` - The Bluetooth data connection.
* - `DUMMY` - Dummy data connection.
* - `ETHERNET` - The Ethernet data connection.
* - `MOBILE` - The Mobile data connection.
* - `MOBILE_DUN` - A DUN-specific Mobile data connection.
* - `MOBILE_HIPRI` - A High Priority Mobile data connection.
* - `MOBILE_MMS` - An MMS-specific Mobile data connection.
* - `MOBILE_SUPL` - A SUPL-specific Mobile data connection.
* - `VPN` - A virtual network using one or more native bearers. Requires API Level 21
* - `WIFI` - The WIFI data connection.
* - `WIMAX` - The WiMAX data connection.
* - `UNKNOWN` - Unknown data connection.
*
* The rest of the connectivity types are hidden by the Android API, but can be used if necessary.
*/
const NetInfo = {
/**
* Invokes the listener whenever network status changes.
* The listener receives one of the connectivity types listed above.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no-trailing-spaces: Trailing spaces not allowed.

* Adds an event handler. Supported events:
*
* - `connectionchange`: Fires when the network status changes. The argument to the event
* handler is an object with keys:
* - `type`: A `ConnectionType` (listed above)
* - `effectiveType`: An `EffectiveConnectionType` (listed above)
* - `change`: This event is deprecated. Listen to `connectionchange` instead. Fires when
* the network status changes. The argument to the event handler is one of the deprecated
* connectivity types listed above.
*/
addEventListener(
eventName: ChangeEventName,
handler: Function
): {remove: () => void} {
const listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler(appStateData.network_info);
}
);
let listener;
if (eventName === 'connectionchange') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the event name is all lower case? And

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll switch it to connectionChange

listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler({
type: appStateData.connectionType,
effectiveType: appStateData.effectiveConnectionType
});
}
);
} else if (eventName === 'change') {
console.warn(`NetInfo's 'change' event is deprecated. Listen to the 'connectionchange' event instead.`);

listener = NetInfoEventEmitter.addListener(
DEVICE_CONNECTIVITY_EVENT,
(appStateData) => {
handler(appStateData.network_info);
}
);
} else {
console.warn(`Trying to subscribe to unknown event: "${type}"`);
return {
remove: () => {}
};
}

_subscriptions.set(handler, listener);
return {
remove: () => NetInfo.removeEventListener(eventName, handler)
Expand All @@ -205,13 +256,28 @@ const NetInfo = {
},

/**
* Returns a promise that resolves with one of the connectivity types listed
* above.
* This function is deprecated. Use `getConnection` instead. Returns a promise that
* resolves with one of the deprecated connectivity types listed above.
*/
fetch(): Promise<any> {
console.warn('NetInfo.fetch() is deprecated. Use NetInfo.getConnection() instead.');
return RCTNetInfo.getCurrentConnectivity().then(resp => resp.network_info);
},

/**
* Returns a promise that resolves to an object with `type` and `effectiveType` keys
* whose values are a `ConnectionType` and an `EffectiveConnectionType`, (described above),
* respectively.
*/
getConnection(): Promise<any> {
return RCTNetInfo.getCurrentConnectivity().then(resp => {
return {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semi: Missing semicolon.

type: resp.connectionType,
effectiveType: resp.effectiveConnectionType,
}
});
},

/**
* An object with the same methods as above but the listener receives a
* boolean which represents the internet connectivity.
Expand Down
74 changes: 63 additions & 11 deletions Libraries/Network/RCTNetInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,26 @@

#import "RCTNetInfo.h"

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>

// Based on the ConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTConnectionTypeUnknown = @"unknown";
static NSString *const RCTConnectionTypeNone = @"none";
static NSString *const RCTConnectionTypeWifi = @"wifi";
static NSString *const RCTConnectionTypeCellular = @"cellular";

// Based on the EffectiveConnectionType enum described in the W3C Network Information API spec
// (https://wicg.github.io/netinfo/).
static NSString *const RCTEffectiveConnectionTypeUnknown = @"unknown";
static NSString *const RCTEffectiveConnectionType2g = @"2g";
static NSString *const RCTEffectiveConnectionType3g = @"3g";
static NSString *const RCTEffectiveConnectionType4g = @"4g";

// The RCTReachabilityState* values are deprecated.
static NSString *const RCTReachabilityStateUnknown = @"unknown";
static NSString *const RCTReachabilityStateNone = @"none";
static NSString *const RCTReachabilityStateWifi = @"wifi";
Expand All @@ -21,7 +37,9 @@
@implementation RCTNetInfo
{
SCNetworkReachabilityRef _reachability;
NSString *_status;
NSString *_connectionType;
NSString *_effectiveConnectionType;
NSString *_statusDeprecated;
NSString *_host;
}

Expand All @@ -30,27 +48,57 @@ @implementation RCTNetInfo
static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
RCTNetInfo *self = (__bridge id)info;
NSString *connectionType = RCTConnectionTypeUnknown;
NSString *effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
NSString *status = RCTReachabilityStateUnknown;
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
connectionType = RCTConnectionTypeNone;
status = RCTReachabilityStateNone;
}

#if TARGET_OS_IPHONE

else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
connectionType = RCTConnectionTypeCellular;
status = RCTReachabilityStateCell;

CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
if (netinfo) {
if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
effectiveConnectionType = RCTEffectiveConnectionType2g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
[netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
effectiveConnectionType = RCTEffectiveConnectionType3g;
} else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
effectiveConnectionType = RCTEffectiveConnectionType4g;
}
}
}

#endif

else {
connectionType = RCTConnectionTypeWifi;
status = RCTReachabilityStateWifi;
}

if (![status isEqualToString:self->_status]) {
self->_status = status;
[self sendEventWithName:@"networkStatusDidChange" body:@{@"network_info": status}];

if (![connectionType isEqualToString:self->_connectionType] ||
![effectiveConnectionType isEqualToString:self->_effectiveConnectionType] ||
![status isEqualToString:self->_statusDeprecated]) {
self->_connectionType = connectionType;
self->_effectiveConnectionType = effectiveConnectionType;
self->_statusDeprecated = status;
[self sendEventWithName:@"networkStatusDidChange" body:@{@"connectionType": connectionType,
@"effectiveConnectionType": effectiveConnectionType,
@"network_info": status}];
}
}

Expand All @@ -74,7 +122,9 @@ - (instancetype)initWithHost:(NSString *)host

- (void)startObserving
{
_status = RCTReachabilityStateUnknown;
_connectionType = RCTConnectionTypeUnknown;
_effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
_statusDeprecated = RCTReachabilityStateUnknown;
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com");
SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(_reachability, RCTReachabilityCallback, &context);
Expand All @@ -94,7 +144,9 @@ - (void)stopObserving
RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject)
{
resolve(@{@"network_info": _status ?: RCTReachabilityStateUnknown});
resolve(@{@"connectionType": _connectionType ?: RCTConnectionTypeUnknown,
@"effectiveConnectionType": _effectiveConnectionType ?: RCTEffectiveConnectionTypeUnknown,
@"network_info": _statusDeprecated ?: RCTReachabilityStateUnknown});
}

@end
Loading