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