1// Copyright © 2018 650 Industries. All rights reserved. 2 3#import <Foundation/FoundationErrors.h> 4 5#import <ExpoModulesCore/EXSingletonModule.h> 6#import <ExpoModulesCore/EXModuleRegistryProvider.h> 7#import <ExpoModulesCore/EXLegacyAppDelegateWrapper.h> 8 9static NSMutableArray<id<UIApplicationDelegate>> *subcontractors; 10static NSMutableDictionary<NSString *,NSArray<id<UIApplicationDelegate>> *> *subcontractorsForSelector; 11static dispatch_once_t onceToken; 12 13@implementation EXLegacyAppDelegateWrapper 14 15@synthesize window = _window; 16 17- (void)forwardInvocation:(NSInvocation *)invocation { 18#if DEBUG 19 SEL selector = [invocation selector]; 20 NSArray<id<UIApplicationDelegate>> *delegatesToBeCalled = [self getSubcontractorsImplementingSelector:selector]; 21 NSString *selectorName = NSStringFromSelector(selector); 22 if ([delegatesToBeCalled count] > 0) { 23 [NSException raise:@"Method not implemented in UIApplicationDelegate" format:@"Some modules: %@ have registered for `%@` UIApplicationDelegate's callback, however, neither your AppDelegate nor %@ can handle this method. You'll need to either implement this method in your AppDelegate or submit a pull request to handle it in %@.", delegatesToBeCalled, selectorName, NSStringFromClass([self class]), NSStringFromClass([self class])]; 24 } 25#endif 26 27 [super forwardInvocation:invocation]; 28} 29 30- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions 31{ 32 BOOL answer = NO; 33 34 SEL selector = @selector(application:didFinishLaunchingWithOptions:); 35 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 36 37 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 38 BOOL subcontractorAnswer = NO; 39 subcontractorAnswer = [subcontractor application:application didFinishLaunchingWithOptions:launchOptions]; 40 answer |= subcontractorAnswer; 41 } 42 43 return answer; 44} 45 46- (void)applicationWillEnterForeground:(UIApplication *)application 47{ 48 SEL selector = @selector(applicationWillEnterForeground:); 49 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 50 51 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 52 [subcontractor applicationWillEnterForeground:application]; 53 } 54} 55 56- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options 57{ 58 59 SEL selector = @selector(application:openURL:options:); 60 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 61 62 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 63 if ([subcontractor application:app openURL:url options:options]) { 64 return YES; 65 } 66 } 67 68 return NO; 69} 70 71- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 72{ 73 SEL selector = @selector(application:performFetchWithCompletionHandler:); 74 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 75 76 __block NSUInteger subcontractorsLeft = [subcontractorsArray count]; 77 __block UIBackgroundFetchResult fetchResult = UIBackgroundFetchResultNoData; 78 __block NSObject *lock = [NSObject new]; 79 80 void (^handler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { 81 @synchronized (lock) { 82 if (result == UIBackgroundFetchResultFailed) { 83 fetchResult = UIBackgroundFetchResultFailed; 84 } else if (fetchResult != UIBackgroundFetchResultFailed && result == UIBackgroundFetchResultNewData) { 85 fetchResult = UIBackgroundFetchResultNewData; 86 } 87 88 subcontractorsLeft--; 89 if (subcontractorsLeft == 0) { 90 completionHandler(fetchResult); 91 } 92 } 93 }; 94 95 if (subcontractorsLeft == 0) { 96 completionHandler(fetchResult); 97 } else { 98 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 99 [subcontractor application:application performFetchWithCompletionHandler:handler]; 100 } 101 } 102} 103 104- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler 105{ 106 SEL selector = @selector(application:continueUserActivity:restorationHandler:); 107 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 108 109 __block NSMutableArray<id<UIUserActivityRestoring>> * _Nullable mergedParams = [NSMutableArray new]; 110 __block NSUInteger subcontractorsLeft = [subcontractorsArray count]; 111 __block NSObject *lock = [NSObject new]; 112 113 void (^handler)(NSArray<id<UIUserActivityRestoring>> * _Nullable) = ^(NSArray<id<UIUserActivityRestoring>> * _Nullable param) { 114 @synchronized (lock) { 115 116 [mergedParams addObjectsFromArray:param]; 117 118 subcontractorsLeft--; 119 if (subcontractorsLeft == 0) { 120 restorationHandler(mergedParams); 121 } 122 } 123 }; 124 125 BOOL result = NO; 126 127 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 128 result = result || [subcontractor application:application continueUserActivity:userActivity restorationHandler:handler]; 129 } 130 return result; 131} 132 133#pragma mark - BackgroundSession 134 135- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler 136{ 137 SEL selector = @selector(application:handleEventsForBackgroundURLSession:completionHandler:); 138 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 139 140 __block BOOL delegatingCompleted = NO; 141 __block int delegatesCompleted = 0; 142 __block unsigned long allDelegates = subcontractorsArray.count; 143 __block void (^completionHandlerCaller)(void) = ^ { 144 if (delegatesCompleted && delegatingCompleted == allDelegates) { 145 completionHandler(); 146 } 147 }; 148 149 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 150 [subcontractor application:application handleEventsForBackgroundURLSession:identifier completionHandler:^(){ 151 @synchronized (self) { 152 delegatesCompleted += 1; 153 completionHandlerCaller(); 154 } 155 }]; 156 } 157 158 @synchronized (self) { 159 delegatingCompleted = YES; 160 completionHandlerCaller(); 161 } 162} 163 164#pragma mark - Notifications 165 166- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token 167{ 168 SEL selector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); 169 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 170 171 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 172 [subcontractor application:application didRegisterForRemoteNotificationsWithDeviceToken:token]; 173 } 174} 175 176- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err 177{ 178 SEL selector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); 179 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 180 181 for(id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 182 [subcontractor application:application didFailToRegisterForRemoteNotificationsWithError:err]; 183 } 184} 185 186- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler 187{ 188 SEL selector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); 189 NSArray<id<UIApplicationDelegate>> *subcontractorsArray = [self getSubcontractorsImplementingSelector:selector]; 190 191 __block NSUInteger subcontractorsLeft = [subcontractorsArray count]; 192 __block UIBackgroundFetchResult fetchResult = UIBackgroundFetchResultNoData; 193 __block NSObject *lock = [NSObject new]; 194 195 void (^handler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) { 196 @synchronized (lock) { 197 if (result == UIBackgroundFetchResultFailed) { 198 fetchResult = UIBackgroundFetchResultFailed; 199 } else if (fetchResult != UIBackgroundFetchResultFailed && result == UIBackgroundFetchResultNewData) { 200 fetchResult = UIBackgroundFetchResultNewData; 201 } 202 203 subcontractorsLeft--; 204 if (subcontractorsLeft == 0) { 205 completionHandler(fetchResult); 206 } 207 } 208 }; 209 210 if (subcontractorsLeft == 0) { 211 completionHandler(fetchResult); 212 } else { 213 for (id<UIApplicationDelegate> subcontractor in subcontractorsArray) { 214 [subcontractor application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:handler]; 215 } 216 } 217} 218 219 220#pragma mark - Subcontractors 221 222- (void)ensureSubcontractorsAreInitializedAndSorted { 223 dispatch_once(&onceToken, ^{ 224 subcontractors = [[NSMutableArray alloc] init]; 225 subcontractorsForSelector = [NSMutableDictionary new]; 226 227 NSArray<EXSingletonModule*> * singletonModules = [[EXModuleRegistryProvider singletonModules] allObjects]; 228 229 for (EXSingletonModule *singletonModule in singletonModules) { 230 if ([singletonModule conformsToProtocol:@protocol(UIApplicationDelegate)]) { 231 [subcontractors addObject:(id<UIApplicationDelegate>)singletonModule]; 232 } 233 } 234 235 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"priority" 236 ascending:NO]; 237 [subcontractors sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; 238 }); 239} 240 241- (NSArray<id<UIApplicationDelegate>> *)getSubcontractorsImplementingSelector:(SEL)selector { 242 243 [self ensureSubcontractorsAreInitializedAndSorted]; 244 245 NSString *selectorKey = NSStringFromSelector(selector); 246 247 if (subcontractorsForSelector[selectorKey]) { 248 return subcontractorsForSelector[selectorKey]; 249 } 250 251 NSMutableArray<id<UIApplicationDelegate>> *result = [NSMutableArray new]; 252 253 for (id<UIApplicationDelegate> subcontractor in subcontractors) { 254 if ([subcontractor respondsToSelector:selector]) { 255 [result addObject:subcontractor]; 256 } 257 } 258 259 subcontractorsForSelector[selectorKey] = result; 260 261 return result; 262} 263 264@end 265