Mehdi Mulani 9d45de60ac Properly tear down the reachabilityRef in RCTNetInfo
Summary: The pull request that added this (#17397) simply forgot to remove the callback, which would cause crashes if the RCTNetInfo module was ever deallocated. While that usually doesn't happen in apps, it can if the user logs out and you need to wipe all the RCT modules (to remove user data, for instance).

Reviewed By: PeteTheHeat

Differential Revision: D7322999

fbshipit-source-id: e49ec7311b39920f7b7743a5854c0dda1dbccc73
2018-03-19 11:00:23 -07:00

175 lines
6.6 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNetInfo.h"
#if !TARGET_OS_TV
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#endif
#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";
static NSString *const RCTReachabilityStateCell = @"cell";
@implementation RCTNetInfo
{
SCNetworkReachabilityRef _reachability;
NSString *_connectionType;
NSString *_effectiveConnectionType;
NSString *_statusDeprecated;
NSString *_host;
BOOL _isObserving;
}
RCT_EXPORT_MODULE()
static void RCTReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
RCTNetInfo *self = (__bridge id)info;
if ([self setReachabilityStatus:flags] && self->_isObserving) {
[self sendEventWithName:@"networkStatusDidChange" body:@{@"connectionType": self->_connectionType,
@"effectiveConnectionType": self->_effectiveConnectionType,
@"network_info": self->_statusDeprecated}];
}
}
#pragma mark - Lifecycle
- (instancetype)initWithHost:(NSString *)host
{
RCTAssertParam(host);
RCTAssert(![host hasPrefix:@"http"], @"Host value should just contain the domain, not the URL scheme.");
if ((self = [self init])) {
_host = [host copy];
}
return self;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"networkStatusDidChange"];
}
- (void)startObserving
{
_isObserving = YES;
_connectionType = RCTConnectionTypeUnknown;
_effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
_statusDeprecated = RCTReachabilityStateUnknown;
_reachability = [self getReachabilityRef];
}
- (void)stopObserving
{
_isObserving = NO;
if (_reachability) {
SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFRelease(_reachability);
}
}
- (SCNetworkReachabilityRef)getReachabilityRef
{
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _host.UTF8String ?: "apple.com");
SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback(reachability, RCTReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
return reachability;
}
- (BOOL)setReachabilityStatus:(SCNetworkReachabilityFlags)flags
{
NSString *connectionType = RCTConnectionTypeUnknown;
NSString *effectiveConnectionType = RCTEffectiveConnectionTypeUnknown;
NSString *status = RCTReachabilityStateUnknown;
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
connectionType = RCTConnectionTypeNone;
status = RCTReachabilityStateNone;
}
#if !TARGET_OS_TV
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 (![connectionType isEqualToString:self->_connectionType] ||
![effectiveConnectionType isEqualToString:self->_effectiveConnectionType] ||
![status isEqualToString:self->_statusDeprecated]) {
self->_connectionType = connectionType;
self->_effectiveConnectionType = effectiveConnectionType;
self->_statusDeprecated = status;
return YES;
}
return NO;
}
#pragma mark - Public API
RCT_EXPORT_METHOD(getCurrentConnectivity:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject)
{
SCNetworkReachabilityRef reachability = [self getReachabilityRef];
SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFRelease(reachability);
resolve(@{@"connectionType": _connectionType ?: RCTConnectionTypeUnknown,
@"effectiveConnectionType": _effectiveConnectionType ?: RCTEffectiveConnectionTypeUnknown,
@"network_info": _statusDeprecated ?: RCTReachabilityStateUnknown});
}
@end