iOS static

此篇是為了註記iOS static

需求

因為是希望把跟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如下

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();
}

Last updated