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 48EX_EXPORT_METHOD_AS(unregisterForNotificationsAsync, 49 unregisterForNotificationsAsync:(EXPromiseResolveBlock)resolve reject:(EXPromiseRejectBlock)reject) 50{ 51 [[UIApplication sharedApplication] unregisterForRemoteNotifications]; 52 resolve(nil); 53} 54 55# pragma mark - EXModuleRegistryConsumer 56 57- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry 58{ 59 _eventEmitter = [moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)]; 60 _pushTokenManager = [moduleRegistry getSingletonModuleForName:@"PushTokenManager"]; 61} 62 63# pragma mark - EXEventEmitter 64 65- (NSArray<NSString *> *)supportedEvents 66{ 67 return @[onDevicePushTokenEventName]; 68} 69 70- (void)startObserving 71{ 72 [self setIsBeingObserved:YES]; 73} 74 75- (void)stopObserving 76{ 77 [self setIsBeingObserved:NO]; 78} 79 80- (BOOL)shouldListen 81{ 82 return _isBeingObserved || _isSettlingPromise; 83} 84 85- (void)updateListeningState 86{ 87 if ([self shouldListen] && !_isListening) { 88 [_pushTokenManager addListener:self]; 89 _isListening = YES; 90 } else if (![self shouldListen] && _isListening) { 91 [_pushTokenManager removeListener:self]; 92 _isListening = NO; 93 } 94} 95 96# pragma mark - EXPushTokenListener 97 98- (void)onDidRegisterWithDeviceToken:(NSData *)devicePushToken 99{ 100 NSMutableString *stringToken = [NSMutableString string]; 101 const char *bytes = [devicePushToken bytes]; 102 for (int i = 0; i < [devicePushToken length]; i++) { 103 [stringToken appendFormat:@"%02.2hhx", bytes[i]]; 104 } 105 106 if (_getDevicePushTokenResolver) { 107 _getDevicePushTokenResolver(stringToken); 108 [self onGetDevicePushTokenPromiseSettled]; 109 } 110 111 if (_isBeingObserved) { 112 [_eventEmitter sendEventWithName:onDevicePushTokenEventName 113 body:@{ @"devicePushToken": stringToken }]; 114 } 115} 116 117- (void)onDidFailToRegisterWithError:(NSError *)error 118{ 119 if (_getDevicePushTokenRejecter) { 120 NSString *message = @"Notification registration failed: "; 121 122 // A common error, localizedDescription may not be helpful. 123 if (error.code == 3000 && [NSCocoaErrorDomain isEqualToString:error.domain]) { 124 message = [message stringByAppendingString:@"\"Push Notifications\" capability hasn't been added to the app in current environment: "]; 125 } 126 127 message = [message stringByAppendingFormat:@"%@", error.localizedDescription]; 128 _getDevicePushTokenRejecter(@"E_REGISTRATION_FAILED", message, error); 129 [self onGetDevicePushTokenPromiseSettled]; 130 } 131} 132 133- (void)onGetDevicePushTokenPromiseSettled 134{ 135 _getDevicePushTokenResolver = nil; 136 _getDevicePushTokenRejecter = nil; 137 [self setIsSettlingPromise:NO]; 138} 139 140# pragma mark - Internal state 141 142- (void)setIsBeingObserved:(BOOL)isBeingObserved 143{ 144 _isBeingObserved = isBeingObserved; 145 [self updateListeningState]; 146} 147 148- (void)setIsSettlingPromise:(BOOL)isSettlingPromise 149{ 150 _isSettlingPromise = isSettlingPromise; 151 [self updateListeningState]; 152} 153 154@end 155