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