UNPKG

8.25 kBPlain TextView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "RNCNetInfo.h"
9#import "RNCConnectionStateWatcher.h"
10
11#include <ifaddrs.h>
12#include <arpa/inet.h>
13
14#if !TARGET_OS_TV
15#import <CoreTelephony/CTCarrier.h>
16#import <CoreTelephony/CTTelephonyNetworkInfo.h>
17#endif
18@import SystemConfiguration.CaptiveNetwork;
19
20#import <React/RCTAssert.h>
21#import <React/RCTBridge.h>
22#import <React/RCTEventDispatcher.h>
23
24@interface RNCNetInfo () <RNCConnectionStateWatcherDelegate>
25
26@property (nonatomic, strong) RNCConnectionStateWatcher *connectionStateWatcher;
27@property (nonatomic) BOOL isObserving;
28@property (nonatomic) NSDictionary *config;
29
30@end
31
32@implementation RNCNetInfo
33
34#pragma mark - Module setup
35
36RCT_EXPORT_MODULE()
37
38// We need RNCReachabilityCallback's and module methods to be called on the same thread so that we can have
39// guarantees about when we mess with the reachability callbacks.
40- (dispatch_queue_t)methodQueue
41{
42 return dispatch_get_main_queue();
43}
44
45+ (BOOL)requiresMainQueueSetup
46{
47 return YES;
48}
49
50#pragma mark - Lifecycle
51
52- (NSArray *)supportedEvents
53{
54 return @[@"netInfo.networkStatusDidChange"];
55}
56
57- (void)startObserving
58{
59 self.isObserving = YES;
60}
61
62- (void)stopObserving
63{
64 self.isObserving = NO;
65}
66
67- (instancetype)init
68{
69 self = [super init];
70 if (self) {
71 _connectionStateWatcher = [[RNCConnectionStateWatcher alloc] initWithDelegate:self];
72 }
73 return self;
74}
75
76- (void)dealloc
77{
78 self.connectionStateWatcher = nil;
79}
80
81#pragma mark - RNCConnectionStateWatcherDelegate
82
83- (void)connectionStateWatcher:(RNCConnectionStateWatcher *)connectionStateWatcher didUpdateState:(RNCConnectionState *)state
84{
85 if (self.isObserving) {
86 NSDictionary *dictionary = [self currentDictionaryFromUpdateState:state withInterface:NULL];
87 [self sendEventWithName:@"netInfo.networkStatusDidChange" body:dictionary];
88 }
89}
90
91#pragma mark - Public API
92
93RCT_EXPORT_METHOD(getCurrentState:(nullable NSString *)requestedInterface resolve:(RCTPromiseResolveBlock)resolve
94 reject:(__unused RCTPromiseRejectBlock)reject)
95{
96 RNCConnectionState *state = [self.connectionStateWatcher currentState];
97 resolve([self currentDictionaryFromUpdateState:state withInterface:requestedInterface]);
98}
99
100RCT_EXPORT_METHOD(configure:(NSDictionary *)config)
101{
102 self.config = config;
103}
104
105#pragma mark - Utilities
106
107// Converts the state into a dictionary to send over the bridge
108- (NSDictionary *)currentDictionaryFromUpdateState:(RNCConnectionState *)state withInterface:(nullable NSString *)requestedInterface
109{
110 NSString *selectedInterface = requestedInterface ?: state.type;
111 NSMutableDictionary *details = [self detailsFromInterface:selectedInterface withState:state];
112 bool connected = [state.type isEqualToString:selectedInterface] && state.connected;
113 if (connected) {
114 details[@"isConnectionExpensive"] = @(state.expensive);
115 }
116
117 return @{
118 @"type": selectedInterface,
119 @"isConnected": @(connected),
120 @"details": details ?: NSNull.null
121 };
122}
123
124- (NSMutableDictionary *)detailsFromInterface:(nonnull NSString *)requestedInterface withState:(RNCConnectionState *)state
125{
126 NSMutableDictionary *details = [NSMutableDictionary new];
127 if ([requestedInterface isEqualToString: RNCConnectionTypeCellular]) {
128 details[@"cellularGeneration"] = state.cellularGeneration ?: NSNull.null;
129 details[@"carrier"] = [self carrier] ?: NSNull.null;
130 } else if ([requestedInterface isEqualToString: RNCConnectionTypeWifi] || [requestedInterface isEqualToString: RNCConnectionTypeEthernet]) {
131 details[@"ipAddress"] = [self ipAddress] ?: NSNull.null;
132 details[@"subnet"] = [self subnet] ?: NSNull.null;
133 #if !TARGET_OS_TV && !TARGET_OS_OSX
134 /*
135 Without one of the conditions needed to use CNCopyCurrentNetworkInfo, it will leak memory.
136 Clients should only set the shouldFetchWiFiSSID to true after ensuring requirements are met to get (B)SSID.
137 */
138 if (self.config && self.config[@"shouldFetchWiFiSSID"]) {
139 details[@"ssid"] = [self ssid] ?: NSNull.null;
140 details[@"bssid"] = [self bssid] ?: NSNull.null;
141 }
142 #endif
143 }
144 return details;
145}
146
147- (NSString *)carrier
148{
149#if (TARGET_OS_TV || TARGET_OS_OSX)
150 return nil;
151#else
152 CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
153 CTCarrier *carrier = [netinfo subscriberCellularProvider];
154 return carrier.carrierName;
155#endif
156}
157
158- (NSString *)ipAddress
159{
160 NSString *address = @"0.0.0.0";
161 struct ifaddrs *interfaces = NULL;
162 struct ifaddrs *temp_addr = NULL;
163 int success = 0;
164 // retrieve the current interfaces - returns 0 on success
165 success = getifaddrs(&interfaces);
166 if (success == 0) {
167 // Loop through linked list of interfaces
168 temp_addr = interfaces;
169 while (temp_addr != NULL) {
170 if (temp_addr->ifa_addr->sa_family == AF_INET) {
171 NSString* ifname = [NSString stringWithUTF8String:temp_addr->ifa_name];
172 if (
173 // Check if interface is en0 which is the wifi connection on the iPhone
174 // and the ethernet connection on the Apple TV
175 [ifname isEqualToString:@"en0"] ||
176 // Check if interface is en1 which is the wifi connection on the Apple TV
177 [ifname isEqualToString:@"en1"]
178 ) {
179 // Get NSString from C String
180 char str[INET_ADDRSTRLEN];
181 inet_ntop(AF_INET, &((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr, str, INET_ADDRSTRLEN);
182 address = [NSString stringWithUTF8String:str];
183 }
184 }
185
186 temp_addr = temp_addr->ifa_next;
187 }
188 }
189 // Free memory
190 freeifaddrs(interfaces);
191 return address;
192}
193
194- (NSString *)subnet
195{
196 NSString *subnet = @"0.0.0.0";
197 struct ifaddrs *interfaces = NULL;
198 struct ifaddrs *temp_addr = NULL;
199 int success = 0;
200 // retrieve the current interfaces - returns 0 on success
201 success = getifaddrs(&interfaces);
202 if (success == 0) {
203 // Loop through linked list of interfaces
204 temp_addr = interfaces;
205 while (temp_addr != NULL) {
206 if (temp_addr->ifa_addr->sa_family == AF_INET) {
207 NSString* ifname = [NSString stringWithUTF8String:temp_addr->ifa_name];
208 if (
209 // Check if interface is en0 which is the wifi connection on the iPhone
210 // and the ethernet connection on the Apple TV
211 [ifname isEqualToString:@"en0"] ||
212 // Check if interface is en1 which is the wifi connection on the Apple TV
213 [ifname isEqualToString:@"en1"]
214 ) {
215 // Get NSString from C String
216 char str[INET_ADDRSTRLEN];
217 inet_ntop(AF_INET, &((struct sockaddr_in *)temp_addr->ifa_netmask)->sin_addr, str, INET_ADDRSTRLEN);
218 subnet = [NSString stringWithUTF8String:str];
219 }
220 }
221
222 temp_addr = temp_addr->ifa_next;
223 }
224 }
225 // Free memory
226 freeifaddrs(interfaces);
227 return subnet;
228}
229
230#if !TARGET_OS_TV && !TARGET_OS_OSX
231- (NSString *)ssid
232{
233 NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
234 NSDictionary *SSIDInfo;
235 NSString *SSID = NULL;
236 for (NSString *interfaceName in interfaceNames) {
237 // CNCopyCurrentNetworkInfo is deprecated for iOS 13+, need to override & use fetchCurrentWithCompletionHandler
238 SSIDInfo = CFBridgingRelease(CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
239 if (SSIDInfo.count > 0) {
240 SSID = SSIDInfo[@"SSID"];
241 if ([SSID isEqualToString:@"Wi-Fi"] || [SSID isEqualToString:@"WLAN"]){
242 SSID = NULL;
243 }
244 break;
245 }
246 }
247 return SSID;
248}
249
250- (NSString *)bssid
251{
252 NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
253 NSDictionary *networkDetails;
254 NSString *BSSID = NULL;
255 for (NSString *interfaceName in interfaceNames) {
256 // CNCopyCurrentNetworkInfo is deprecated for iOS 13+, need to override & use fetchCurrentWithCompletionHandler
257 networkDetails = CFBridgingRelease(CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
258 if (networkDetails.count > 0)
259 {
260 BSSID = networkDetails[(NSString *) kCNNetworkInfoKeyBSSID];
261 break;
262 }
263 }
264 return BSSID;
265}
266#endif
267
268@end