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