# iOS static

### 需求

&#x20;因為是希望把跟server註冊APNs token這段拿到React Native JS Code這邊做掉，所以需要APNs token的取得和通知（當token更新或者其他原因更換了..得通知到JS Code） ps.這裡的通知就是上方的寫法內容

因為怕ios Native已取得token，而後JS Code才監聽(addListener)完成，所以這邊為了保險一點，在iOS Native內加上static參數去儲存token，並且在App第一次開啟時先去獲取一次該static token

還有加上EventEmitter測試的Code

### 完整Code如下&#x20;

APNsTokenEmitter.h

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

@interface APNsTokenEmitter : RCTEventEmitter<RCTBridgeModule>
@property (nonatomic, retain) NSString *apnsToken;
- (void)apnsTokenReceive:(NSString *)token;
@end
```

\
APNsTokenEmitter.m

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

@implementation APNsTokenEmitter
{
  bool hasListeners;
}

RCT_EXPORT_MODULE();
@synthesize apnsToken;
/**
 想再其它.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
{
  self.apnsToken = token;
  if (hasListeners) {
    [self sendEventWithName:@"APNsToken" body:token];
    RCTLogInfo(@"APNsTokenEmitter apnsTokenReceive(Has Listener), %@", token);
  }else {
    RCTLogInfo(@"APNsTokenEmitterapnsTokenReceive(No Listener), %@", token);
  }
}

@end
```

\
AppDelegat.h

```
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;

@end
```

\
AppDelegat.m（內包含註冊APNs token的code，詳細還是參考註冊那篇文章）

```
#import "AppDelegate.h"

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

#import "APNsTokenEmitter.h"
#import "Orientation.h"
#import "SplashScreen.h"
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    NSLog(@"Requesting permission for push notifications..."); // iOS 8
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:
                                            UIUserNotificationTypeAlert | UIUserNotificationTypeBadge |
                                            UIUserNotificationTypeSound categories:nil];
    [UIApplication.sharedApplication registerUserNotificationSettings:settings];
  } else {
    NSLog(@"Registering device for push notifications..."); // iOS 7 and earlier
    [UIApplication.sharedApplication registerForRemoteNotificationTypes:
     UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge |
     UIRemoteNotificationTypeSound];
  }

  // ????去掉這段 螢幕就黑的... （這段應該是我自己寫的Alert啊..
  NSURL *jsCodeLocation;
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"AllPayPassRN"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  //why...

  [SplashScreen show];  // 添加这一句，这一句一定要在最后
  return YES;
}

- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)settings
{
  NSLog(@"Registering device for push notifications..."); // iOS 8
  [application registerForRemoteNotifications];
}

- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
  //將token parser好
  NSString * deviceTokenString = [[[[token description]
                                    stringByReplacingOccurrencesOfString: @"<" withString: @""]
                                   stringByReplacingOccurrencesOfString: @">" withString: @""]
                                  stringByReplacingOccurrencesOfString: @" " withString: @""];
  NSLog(@"Registration successful, bundle identifier: %@, mode: %@, device token: %@",
        [NSBundle.mainBundle bundleIdentifier], [self modeString], deviceTokenString);

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

- (void)application:(UIApplication *)application
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  NSLog(@"Failed to register: %@", error);
}

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)notification completionHandler:(void(^)())completionHandler
{
  NSLog(@"Received push notification: %@, identifier: %@", notification, identifier); // iOS 8
  completionHandler();
}

- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)notification
{
  NSLog(@"Received push notification: %@", notification); // iOS 7 and earlier
}

- (NSString *)modeString
{
#if DEBUG
  return @"Development (sandbox)";
#else
  return @"Production";
#endif
}
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  return [Orientation getOrientation];
}

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

@end
```

\
AllPayPassMethod.h （這是我曝露給RN Code的Module）

```
//匯入RCTBridgeModule標頭
#import <React/RCTBridgeModule.h>
//宣告AllPayPassMethod類別是NSObject的子類別並實作RCTBridgeModule界面，以@end結束界面得宣告。
@interface AllPayPassMethod : NSObject <RCTBridgeModule>
@end
```

\
AllPayPassMethod.m

```
#import "AllPayPassMethod.h"
#import <React/RCTLog.h>
#import "IntellaSDK/IntellaSDK.h"
#import "APNsTokenEmitter.h"

//@implementation 與 @end 行表示於之間的內容是AllPayPassMethod的類別實作。
@implementation AllPayPassMethod
// To export a module named AllPayPassMethod。叫用RN的巨集已讓此類別可被RN存取
RCT_EXPORT_MODULE();

//同樣的，bridgeTest :str的方法定義也已RCT_EXPORT_METHOD巨集前綴，他匯出此方法已顯露給我們的js程式。
//每個參數名稱包含在方法名稱中。
RCT_EXPORT_METHOD(bridgeTest:(NSString*) str){
   RCTLogInfo(@"bridgeTest, %@", str);
}

RCT_EXPORT_METHOD(getPhoneOnlyNumber: (RCTResponseSenderBlock)callback){
  NSString *UDID=[allpaylib getUDID];
//  NSLog(@"UDID=%@",UDID);
  callback(@[UDID]);
}

RCT_EXPORT_METHOD(emitterAPNsTokenTest:(NSString*) str){
  RCTLogInfo(@"emitterAPNsTokenTest, %@", str);
  APNsTokenEmitter *tokenEmitter = [APNsTokenEmitter allocWithZone:nil];
  [tokenEmitter apnsTokenReceive:str];
}

RCT_EXPORT_METHOD(getAPNsToken:(RCTResponseSenderBlock)callback){
  APNsTokenEmitter *tokenEmitter = [APNsTokenEmitter allocWithZone:nil];
  RCTLogInfo(@"getAPNsToken, %@", tokenEmitter.apnsToken);
  if(tokenEmitter.apnsToken == nil) {
    callback(@[@""]);
  }else {
    callback(@[tokenEmitter.apnsToken]);
  }
}

@end
```

\
RN JS Code (只貼部分code)

```
import {NativeModules, NativeEventEmitter} from 'react-native’

const {APNsTokenEmitter, AllPayPassMethod} = NativeModules

...

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

...

componentWillUnmount() {
    subscription.remove();
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://twins-bamboo.gitbook.io/react-native-note/basic-ii/eventeimtter-ios/ios-static.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
