1// Copyright 2018-present 650 Industries. All rights reserved. 2 3#import <EXNotifications/EXPushTokenModule.h> 4#import <EXNotifications/EXPushTokenManager.h> 5 6#import <ExpoModulesCore/EXEventEmitterService.h> 7 8static NSString * const onDevicePushTokenEventName = @"onDevicePushToken"; 9 10@interface EXPushTokenModule () 11 12@property (nonatomic, weak) id<EXPushTokenManager> pushTokenManager; 13 14@property (nonatomic, assign) BOOL isListening; 15@property (nonatomic, assign) BOOL isBeingObserved; 16@property (nonatomic, assign) BOOL isSettlingPromise; 17 18@property (nonatomic, weak) id<EXEventEmitterService> eventEmitter; 19 20@property (nonatomic, strong) EXPromiseResolveBlock getDevicePushTokenResolver; 21@property (nonatomic, strong) EXPromiseRejectBlock getDevicePushTokenRejecter; 22 23@end 24 25@implementation EXPushTokenModule 26 27EX_EXPORT_MODULE(ExpoPushTokenManager); 28 29# pragma mark - Exported methods 30 31EX_EXPORT_METHOD_AS(getDevicePushTokenAsync, 32 getDevicePushTokenResolving:(EXPromiseResolveBlock)resolve rejecting:(EXPromiseRejectBlock)reject) 33{ 34 if (_getDevicePushTokenRejecter) { 35 reject(@"E_AWAIT_PROMISE", @"Another async call to this method is in progress. Await the first Promise.", nil); 36 return; 37 } 38 39 _getDevicePushTokenResolver = resolve; 40 _getDevicePushTokenRejecter = reject; 41 [self setIsSettlingPromise:YES]; 42 43 dispatch_async(dispatch_get_main_queue(), ^{ 44 [[UIApplication sharedApplication] registerForRemoteNotifications]; 45 }); 46} 47 48# pragma mark - EXModuleRegistryConsumer 49 50- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry 51{ 52 _eventEmitter = [moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)]; 53 _pushTokenManager = [moduleRegistry getSingletonModuleForName:@"PushTokenManager"]; 54} 55 56# pragma mark - EXEventEmitter 57 58- (NSArray<NSString *> *)supportedEvents 59{ 60 return @[onDevicePushTokenEventName]; 61} 62 63- (void)startObserving 64{ 65 [self setIsBeingObserved:YES]; 66} 67 68- (void)stopObserving 69{ 70 [self setIsBeingObserved:NO]; 71} 72 73- (BOOL)shouldListen 74{ 75 return _isBeingObserved || _isSettlingPromise; 76} 77 78- (void)updateListeningState 79{ 80 if ([self shouldListen] && !_isListening) { 81 [_pushTokenManager addListener:self]; 82 _isListening = YES; 83 } else if (![self shouldListen] && _isListening) { 84 [_pushTokenManager removeListener:self]; 85 _isListening = NO; 86 } 87} 88 89# pragma mark - EXPushTokenListener 90 91- (void)onDidRegisterWithDeviceToken:(NSData *)devicePushToken 92{ 93 NSMutableString *stringToken = [NSMutableString string]; 94 const char *bytes = [devicePushToken bytes]; 95 for (int i = 0; i < [devicePushToken length]; i++) { 96 [stringToken appendFormat:@"%02.2hhx", bytes[i]]; 97 } 98 99 if (_getDevicePushTokenResolver) { 100 _getDevicePushTokenResolver(stringToken); 101 [self onGetDevicePushTokenPromiseSettled]; 102 } 103 104 if (_isBeingObserved) { 105 [_eventEmitter sendEventWithName:onDevicePushTokenEventName 106 body:@{ @"devicePushToken": stringToken }]; 107 } 108} 109 110- (void)onDidFailToRegisterWithError:(NSError *)error 111{ 112 if (_getDevicePushTokenRejecter) { 113 NSString *message = @"Notification registration failed: "; 114 115 // A common error, localizedDescription may not be helpful. 116 if (error.code == 3000 && [NSCocoaErrorDomain isEqualToString:error.domain]) { 117 message = [message stringByAppendingString:@"\"Push Notifications\" capability hasn't been added to the app in current environment: "]; 118 } 119 120 message = [message stringByAppendingFormat:@"%@", error.localizedDescription]; 121 _getDevicePushTokenRejecter(@"E_REGISTRATION_FAILED", message, error); 122 [self onGetDevicePushTokenPromiseSettled]; 123 } 124} 125 126- (void)onGetDevicePushTokenPromiseSettled 127{ 128 _getDevicePushTokenResolver = nil; 129 _getDevicePushTokenRejecter = nil; 130 [self setIsSettlingPromise:NO]; 131} 132 133# pragma mark - Internal state 134 135- (void)setIsBeingObserved:(BOOL)isBeingObserved 136{ 137 _isBeingObserved = isBeingObserved; 138 [self updateListeningState]; 139} 140 141- (void)setIsSettlingPromise:(BOOL)isSettlingPromise 142{ 143 _isSettlingPromise = isSettlingPromise; 144 [self updateListeningState]; 145} 146 147@end 148