react-native-zeroconf 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,11 @@
13
13
 
14
14
  @property (nonatomic, strong, readonly) NSMutableDictionary *resolvingServices;
15
15
  @property (nonatomic, strong, readonly) NSMutableDictionary *publishedServices;
16
+ // Services already retried after a resolve timeout
17
+ @property (nonatomic, strong, readonly) NSMutableSet<NSString *> *retriedServices;
18
+ // Stopped browsers, kept alive until their asynchronous stop has been processed
19
+ @property (nonatomic, strong, readonly) NSMutableSet<NSNetServiceBrowser *> *stoppingBrowsers;
20
+ @property (nonatomic, assign) NSTimeInterval resolveTimeoutSeconds;
16
21
 
17
22
  @end
18
23
 
@@ -25,13 +30,34 @@ RCT_EXPORT_MODULE()
25
30
  RCT_EXPORT_METHOD(scan:(NSString *)type protocol:(NSString *)protocol domain:(NSString *)domain)
26
31
  {
27
32
  [self stop];
33
+
34
+ // A fresh browser for each scan, restarting a search on a browser that is still stopping can crash (#154)
35
+ self.browser = [[NSNetServiceBrowser alloc] init];
36
+ [self.browser setDelegate:self];
28
37
  [self.browser searchForServicesOfType:[NSString stringWithFormat:@"_%@._%@.", type, protocol] inDomain:domain];
29
38
  }
30
39
 
31
40
  RCT_EXPORT_METHOD(stop)
32
41
  {
33
- [self.browser stop];
42
+ NSNetServiceBrowser *browser = self.browser;
43
+ if (browser) {
44
+ self.browser = nil;
45
+ [self.stoppingBrowsers addObject:browser];
46
+ [browser stop];
47
+
48
+ // The stop completes asynchronously on the run loop, release the browser well after it
49
+ __weak RNZeroconf *weakSelf = self;
50
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
51
+ browser.delegate = nil;
52
+ [weakSelf.stoppingBrowsers removeObject:browser];
53
+ });
54
+ }
55
+ for (NSNetService *service in self.resolvingServices.allValues) {
56
+ service.delegate = nil;
57
+ [service stop];
58
+ }
34
59
  [self.resolvingServices removeAllObjects];
60
+ [self.retriedServices removeAllObjects];
35
61
  }
36
62
 
37
63
  - (dispatch_queue_t)methodQueue
@@ -93,7 +119,7 @@ RCT_EXPORT_METHOD(unregisterService:(NSString *) serviceName)
93
119
  self.resolvingServices[service.name] = service;
94
120
 
95
121
  service.delegate = self;
96
- [service resolveWithTimeout:5.0];
122
+ [service resolveWithTimeout:self.resolveTimeoutSeconds];
97
123
  }
98
124
 
99
125
  // When a service is removed.
@@ -127,7 +153,10 @@ RCT_EXPORT_METHOD(unregisterService:(NSString *) serviceName)
127
153
  // When the search stops.
128
154
  - (void) netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
129
155
  {
130
- [self.bridge.eventDispatcher sendDeviceEventWithName:@"RNZeroconfStop" body:nil];
156
+ // A browser replaced by a newer scan stopping late shouldn't report the new scan as stopped
157
+ if (self.browser == nil || self.browser == browser) {
158
+ [self.bridge.eventDispatcher sendDeviceEventWithName:@"RNZeroconfStop" body:nil];
159
+ }
131
160
  }
132
161
 
133
162
  // When the search starts.
@@ -146,12 +175,22 @@ RCT_EXPORT_METHOD(unregisterService:(NSString *) serviceName)
146
175
 
147
176
  sender.delegate = nil;
148
177
  [self.resolvingServices removeObjectForKey:sender.name];
178
+ [self.retriedServices removeObject:sender.name];
149
179
  }
150
180
 
151
181
  // When the service has failed to resolve it's network data (IP addresses, etc)
152
182
  - (void) netService:(NSNetService *)sender
153
183
  didNotResolve:(NSDictionary *)errorDict
154
184
  {
185
+ // Slow devices can time out, retry once before reporting the error (#186)
186
+ NSNumber *code = errorDict[NSNetServicesErrorCode];
187
+ if (code.integerValue == NSNetServicesTimeoutError && ![self.retriedServices containsObject:sender.name]) {
188
+ [self.retriedServices addObject:sender.name];
189
+ [sender resolveWithTimeout:self.resolveTimeoutSeconds];
190
+ return;
191
+ }
192
+ [self.retriedServices removeObject:sender.name];
193
+
155
194
  [self reportError:errorDict];
156
195
 
157
196
  sender.delegate = nil;
@@ -205,8 +244,9 @@ RCT_EXPORT_METHOD(unregisterService:(NSString *) serviceName)
205
244
  if (self) {
206
245
  _resolvingServices = [[NSMutableDictionary alloc] init];
207
246
  _publishedServices = [[NSMutableDictionary alloc] init];
208
- _browser = [[NSNetServiceBrowser alloc] init];
209
- [_browser setDelegate:self];
247
+ _retriedServices = [[NSMutableSet alloc] init];
248
+ _stoppingBrowsers = [[NSMutableSet alloc] init];
249
+ _resolveTimeoutSeconds = 5.0;
210
250
  }
211
251
 
212
252
  return self;
@@ -235,12 +275,11 @@ RCT_EXPORT_METHOD(unregisterService:(NSString *) serviceName)
235
275
  // Called when the bridge is torn down (e.g. reload): stop scanning and unpublish services
236
276
  - (void) invalidate
237
277
  {
238
- [self.browser stop];
239
- for (NSNetService *service in self.resolvingServices.allValues) {
240
- service.delegate = nil;
241
- [service stop];
278
+ [self stop];
279
+ // Delegates are unretained, detach them so stopping browsers never call a released module
280
+ for (NSNetServiceBrowser *browser in self.stoppingBrowsers) {
281
+ browser.delegate = nil;
242
282
  }
243
- [self.resolvingServices removeAllObjects];
244
283
  for (NSNetService *service in self.publishedServices.allValues) {
245
284
  service.delegate = nil;
246
285
  [service stop];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-zeroconf",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "A Zeroconf discovery utility for react-native",
5
5
  "main": "dist",
6
6
  "types": "index.d.ts",