EventEimtter - iOS

此篇需求:若收到APNs token時,需要通知React Native JS code

要用到的有以下檔案

  • APNsTokenEmitter.h - EventEmitter的標頭檔

  • APNsTokenEmitter.m - EventEmitter 實作

  • Use.h - 要暴露給JS Code使用的標頭檔)

  • Use.m(要使用的.m檔案內) - 要暴露給RN JS Code使用的實作(這邊我是把EventEmitter封裝進此檔案內

  • rnJsCode.js (RN的JS code) - 使用EventEimtter的JS Code

新建檔案(New File) APNsTokenEmitter.h

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface APNsTokenEmitter : RCTEventEmitter<RCTBridgeModule>
- (void)apnsTokenReceive:(NSString *)token;
@end

新建檔案(New File) APNsTokenEmitter.m

#import "APNsTokenEmitter.h"
#import <React/RCTLog.h>

@implementation APNsTokenEmitter
{
  bool hasListeners;
}

RCT_EXPORT_MODULE();

/**
 想再其它.m檔內使用,得先取得APNsTokenEmitter,但用下方方法初始化會有Error
  APNsTokenEmitter * tokenEmitter = [[APNsTokenEmitter alloc] init];

 請參考 https://github.com/facebook/react-native/issues/15421 解法(singleton pattern)
 **/
+ (id)allocWithZone:(NSZone *)zone {
  static APNsTokenEmitter *sharedInstance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [super allocWithZone:zone];
  });
  return sharedInstance;
}

-(NSArray<NSString *> *)supportedEvents
{
  return @[@"APNsToken"];
}

// Will be called when this module's first listener is added.
-(void)startObserving {
  RCTLogInfo(@"APNsTokenEmitter startObserving");
  hasListeners = YES;
  // Set up any upstream listeners or background tasks as necessary
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
  RCTLogInfo(@"APNsTokenEmitter stopObserving");
  hasListeners = NO;
  // Remove upstream listeners, stop unnecessary background tasks
}

-(void)apnsTokenReceive:(NSString *)token
{
  if (hasListeners) {
    [self sendEventWithName:@"APNsToken" body:token];
    RCTLogInfo(@"APNsTokenEmitter apnsTokenReceive(Has Listener), %@", token);
  }else {
    RCTLogInfo(@"APNsTokenEmitterapnsTokenReceive(No Listener), %@", token);
  }
}

@end

ps. startObserving 和 stopObserving fun為優化,詳請參考 官網此網址

要使用的.m檔案內

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#import “APNsTokenEmitter.h"

@implementation AppDelegate

...

- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
    //將token parser好
   NSString * deviceTokenString = [[[[token description]
                                    stringByReplacingOccurrencesOfString: @"<" withString: @""]
                                   stringByReplacingOccurrencesOfString: @">" withString: @""]
                                  stringByReplacingOccurrencesOfString: @" " withString: @""];

   //pass deviceTokenString to function and sending events to javascript
   [self sendAPNsTokenToJS:deviceTokenString];
}

//call APNsTokenEmitter
-(void)sendAPNsTokenToJS:(NSString *)token
{
  APNsTokenEmitter *tokenEmitter = [APNsTokenEmitter allocWithZone:nil];
  [tokenEmitter apnsTokenReceive:token];
}

....

@end

JS code

import { NativeEventEmitter, NativeModules } from 'react-native'
const {APNsTokenEmitter} = NativeModules

...

componentDidMount() {
    this.eventEmitter = new NativeEventEmitter(NativeModules.APNsTokenEmitter);
    this.eventEmitter.addListener('APNsToken', (token) => console.log(`token來也~~${token}`))
}

...

componentWillUnmount() {
    subscription.remove();
}

參考

Last updated