1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
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 |
|
56 | typedef void (^RNCConnectionStateUpdater)(SCNetworkReachabilityFlags);
|
57 |
|
58 | static 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 |
|
66 | static void RNCReachabilityContextRelease(const void *info)
|
67 | {
|
68 | Block_release(info);
|
69 | }
|
70 |
|
71 | static 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
|