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 "RNCConnectionStateWatcher.h" 9#import <SystemConfiguration/SystemConfiguration.h> 10#import <netinet/in.h> 11 12@interface RNCConnectionStateWatcher () <NSURLSessionDataDelegate> 13 14@property (nonatomic) SCNetworkReachabilityRef reachabilityRef; 15@property (nullable, weak, nonatomic) id<RNCConnectionStateWatcherDelegate> delegate; 16@property (nonatomic) SCNetworkReachabilityFlags lastFlags; 17@property (nonnull, strong, nonatomic) RNCConnectionState *state; 18 19@end 20 21@implementation RNCConnectionStateWatcher 22 23#pragma mark - Lifecycle 24 25- (instancetype)initWithDelegate:(id<RNCConnectionStateWatcherDelegate>)delegate 26{ 27 self = [self init]; 28 if (self) { 29 _delegate = delegate; 30 _state = [[RNCConnectionState alloc] init]; 31 _reachabilityRef = [self createReachabilityRef]; 32 } 33 return self; 34} 35 36- (void)dealloc 37{ 38 self.delegate = nil; 39 40 if (self.reachabilityRef != nil) { 41 SCNetworkReachabilityUnscheduleFromRunLoop(self.reachabilityRef, CFRunLoopGetMain(), kCFRunLoopCommonModes); 42 CFRelease(self.reachabilityRef); 43 self.reachabilityRef = nil; 44 } 45} 46 47#pragma mark - Public methods 48 49- (RNCConnectionState *)currentState 50{ 51 return self.state; 52} 53 54#pragma mark - Callback 55 56typedef void (^RNCConnectionStateUpdater)(SCNetworkReachabilityFlags); 57 58static void RNCReachabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) 59{ 60 RNCConnectionStateUpdater block = (__bridge id)info; 61 if (block != nil) { 62 block(flags); 63 } 64} 65 66static void RNCReachabilityContextRelease(const void *info) 67{ 68 Block_release(info); 69} 70 71static const void *RNCReachabilityContextRetain(const void *info) 72{ 73 return Block_copy(info); 74} 75 76- (void)update:(SCNetworkReachabilityFlags)flags 77{ 78 self.lastFlags = flags; 79 self.state = [[RNCConnectionState alloc] initWithReachabilityFlags:flags]; 80} 81 82#pragma mark - Setters 83 84- (void)setState:(RNCConnectionState *)state 85{ 86 if (![state isEqualToConnectionState:_state]) { 87 _state = state; 88 89 [self updateDelegate]; 90 } 91} 92 93#pragma mark - Utilities 94 95- (void)updateDelegate 96{ 97 [self.delegate connectionStateWatcher:self didUpdateState:self.state]; 98} 99 100- (SCNetworkReachabilityRef)createReachabilityRef 101{ 102 struct sockaddr_in zeroAddress; 103 bzero(&zeroAddress, sizeof(zeroAddress)); 104 zeroAddress.sin_len = sizeof(zeroAddress); 105 zeroAddress.sin_family = AF_INET; 106 107 SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *) &zeroAddress); 108 109 __weak typeof(self) weakSelf = self; 110 RNCConnectionStateUpdater callback = ^(SCNetworkReachabilityFlags flags) { 111 __strong __typeof(weakSelf) strongSelf = weakSelf; 112 if (strongSelf != nil) { 113 [strongSelf update:flags]; 114 } 115 }; 116 117 SCNetworkReachabilityContext context = { 118 0, 119 (__bridge void *)callback, 120 RNCReachabilityContextRetain, 121 RNCReachabilityContextRelease, 122 NULL 123 }; 124 SCNetworkReachabilitySetCallback(reachability, RNCReachabilityCallback, &context); 125 SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); 126 127 // Set the state the first time 128 SCNetworkReachabilityFlags flags; 129 SCNetworkReachabilityGetFlags(reachability, &flags); 130 [self update:flags]; 131 132 return reachability; 133} 134 135@end 136