grivescorbett grivescorbett - 5 months ago 118
Swift Question

React native bridge is sometimes nil in swift module

I have a swift module created, which starts listening on a GCDAsyncUdpSocket when a connect method is called from swift

@objc(MyModule)
class MyModule: NSObject, GCDAsyncUdpSocketDelegate {
var bridge: RCTBridge!
var socket: GCDAsyncUdpSocket!

func methodQueue() -> dispatch_queue_t {
return dispatch_queue_create("com.mycompany.greatapp", DISPATCH_QUEUE_SERIAL)
}

@objc func connect(resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: methodQueue())

//...start listening, etc
}

@objc func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
bridge.eventDispatcher().sendAppEventWithName("got_msg", body: nil)
}
}


I've also created a private implementation

#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(MyModule, NSObject)

RCT_EXTERN_METHOD(connect resolver:(RCTPromiseResolveBlock *)resolve
rejecter:(RCTPromiseRejectBlock *)reject)

@end


However on occasion
bridge.eventDispatcher()
unwraps to nil and it is unable to broadcast the event. Any thoughts would be appreciated.

Answer

This Github issue led me to the solution: https://github.com/facebook/react-native/issues/3454. Turns out this was only happening on reload. One needs to implement RCTInvalidating and then clean up any dangling references in invalidate. This allows ARC to cleanup your native module properly and reinstantiate RCTBridge

@objc(MyModule)
class MyModule: NSObject, GCDAsyncUdpSocketDelegate, RCTInvalidating {
  var bridge: RCTBridge!
  var socket: GCDAsyncUdpSocket!

  func invalidate() {
    self.socket = nil
  }

  func methodQueue() -> dispatch_queue_t {
    return dispatch_queue_create("com.mycompany.greatapp", DISPATCH_QUEUE_SERIAL)
  }

  @objc func connect(resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) {
    socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: methodQueue())

    //...start listening, etc
  }

  @objc func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {
    bridge.eventDispatcher().sendAppEventWithName("got_msg", body: nil)
  }
}
Comments