javascript – React Native Native Module Occasion Emission Points with iOS

0
1
javascript – React Native Native Module Occasion Emission Points with iOS


Setup

I’ve a local module that extends RCTEventEmitter and in addition implements a TurboModule spec:

RCTNativeLocalStorage.h:

#import 
#import 

NS_ASSUME_NONNULL_BEGIN
@interface RCTNativeLocalStorage : RCTEventEmitter 
@finish
NS_ASSUME_NONNULL_END

RCTNativeLocalStorage.m (partial):

#import "RCTNativeLocalStorage.h"
#import 
#import 

utilizing namespace fb;

@interface RCTNativeLocalStorage () 
@property (sturdy, nonatomic) NSUserDefaults *localStorage;
@property (sturdy, nonatomic) CBCentralManager *centralManager;
@property (sturdy, nonatomic) NSMutableArray *discoveredDevices;
@property (nonatomic, assign) BOOL hasListeners;
@finish

@implementation RCTNativeLocalStorage

// Register the module
RCT_EXPORT_MODULE(NativeLocalStorage)

// These strategies are required for NativeEventEmitter to work correctly
RCT_EXPORT_METHOD(addListener:(NSString *)eventName)
{
  NSLog(@"🎧 addListener known as for: %@", eventName);
}

RCT_EXPORT_METHOD(removeListeners:(double)rely)
{
  NSLog(@"🔕 removeListeners known as: %f", rely);
}

// Outline supported occasions
- (NSArray *)supportedEvents {
  return @[
    @"BluetoothDeviceFound",
  ];
}

// Occasion listener monitoring
- (void)startObserving {
  NSLog(@"✅ startObserving known as - occasions might be emitted");
  self.hasListeners = YES;
}

- (void)stopObserving {
  NSLog(@"⚠️ stopObserving known as - occasions won't be emitted");
  self.hasListeners = NO;
}

- (instancetype)init {
  if (self = [super init]) {
    _localStorage = [[NSUserDefaults alloc] initWithSuiteName:@"local-storage"];
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    _discoveredDevices = [NSMutableArray new];
    _hasListeners = NO;
  }
  return self;
}

+ (BOOL)requiresMainQueueSetup {
  return NO;
}

// TurboModule implementation
- (std::shared_ptr<:turbomodule>)getTurboModule:(const fb::react::ObjCTurboModule::InitParams &)params {
  return std::make_shared<:nativelocalstoragespecjsi>(params);
}

// MARK: - TurboModule Strategies

- (NSString * _Nullable)getItem:(NSString *)key {
  return [self.localStorage stringForKey:key];
}

- (void)setItem:(NSString *)worth key:(NSString *)key {
  [self.localStorage setObject:value forKey:key];
}

- (void)removeItem:(NSString *)key {
  [self.localStorage removeObjectForKey:key];
}

- (void)clear {
  NSDictionary *allItems = [self.localStorage dictionaryRepresentation];
  for (NSString *key in allItems.allKeys) {
    [self.localStorage removeObjectForKey:key];
  }
}

// Export the startScan methodology to make it accessible to JavaScript
RCT_EXPORT_METHOD(startScan) {
  NSLog(@"✅ startScan triggered from JavaScript");
  
  if (_centralManager.state != CBManagerStatePoweredOn) {
    NSLog(@"❌ Bluetooth not powered on");
    return;
  }
  
  // Present an alert to confirm the strategy was known as
  dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Scan"
                                                                   message:@"startScan called!"
                                                            preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okay = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:ok];
    UIViewController *root = UIApplication.sharedApplication.keyWindow.rootViewController;
    [root presentViewController:alert animated:YES completion:nil];
  });
  
  [_discoveredDevices removeAllObjects];
  [_centralManager scanForPeripheralsWithServices:nil options:nil];
}

// Central Supervisor Delegates
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
  swap (central.state) {
    case CBManagerStatePoweredOn:
      NSLog(@"✅ Bluetooth is powered on.");
      break;
    case CBManagerStatePoweredOff:
      NSLog(@"❌ Bluetooth is powered off.");
      break;
    default:
      NSLog(@"⚠️ Bluetooth state modified: %ld", (lengthy)central.state);
      break;
  }
}

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {

  NSString *deviceName = peripheral.title ?: @"Unknown";
  NSString *deviceId = peripheral.identifier.UUIDString;

  NSDictionary *deviceInfo = @{
    @"title": deviceName,
    @"id": deviceId
  };

  BOOL alreadyExists = NO;
  for (NSDictionary *existingDevice in _discoveredDevices) {
    if ([existingDevice[@"id"] isEqualToString:deviceId]) {
      alreadyExists = YES;
      break;
    }
  }
  
  if (!alreadyExists) {
    [_discoveredDevices addObject:deviceInfo];
    NSLog(@"✅ Machine found: %@", deviceInfo);
    
    // Ship occasion straight on the principle thread
    dispatch_async(dispatch_get_main_queue(), ^{
      // The hasListeners test is necessary to keep away from the warning
      if (self.hasListeners) {
        NSLog(@"🚀 Sending BluetoothDeviceFound occasion");
        [self sendEventWithName:@"BluetoothDeviceFound" body:deviceInfo];
      } else {
        NSLog(@"⚠️ No listeners registered for BluetoothDeviceFound occasion");
      }
    });
  }
}

@finish

Javascript Code:


const { NativeLocalStorage } = NativeModules;
const eventEmitter = new NativeEventEmitter(NativeLocalStorage);

useEffect(() => {
  console.log('Organising occasion listener...');
  
  const subscription = eventEmitter.addListener(
    'BluetoothDeviceFound',
    (deviceInfo) => {
      console.log('Machine discovered:', deviceInfo);
      // Replace state...
    }
  );
  
  console.log('Beginning scan...');
  NativeLocalStorage.startScan();
  
  return () => subscription.take away();
}, []);

Console output after i set off begin scan

✅ Bluetooth is powered on.
'`new NativeEventEmitter()` was known as with a non-null argument with out the required `addListener` methodology.', { [Component Stack] title: 'Part Stack' }
'`new NativeEventEmitter()` was known as with a non-null argument with out the required `removeListeners` methodology.', { [Component Stack] title: 'Part Stack' }
✅ startScan triggered from JavaScript
✅ Machine found: {
    id = "E2DEF552-4C7E-FA6F-1CC3-3F6B0DE3CC31";
    title = Unknown;
}
⚠️ No listeners registered for BluetoothDeviceFound occasion
⚠️ No listeners registered for BluetoothDeviceFound occasion
⚠️ No listeners registered for BluetoothDeviceFound occasion

The Drawback

Unable to ship occasions from iOS native code.
The identical Code works with android and I’m able to ship information to the occasion listeners.

What do you suppose I’m lacking right here?

Official docs hyperlink: https://reactnative.dev/docs/0.77/legacy/native-modules-ios#sending-events-to-javascript

LEAVE A REPLY

Please enter your comment!
Please enter your name here