164 lines
5.7 KiB
Mathematica
164 lines
5.7 KiB
Mathematica
|
// Copyright 2018-present 650 Industries. All rights reserved.
|
||
|
|
||
|
#import <EXSplashScreen/EXSplashScreenModule.h>
|
||
|
#import <EXSplashScreen/EXSplashScreenService.h>
|
||
|
#import <React/RCTRootView.h>
|
||
|
#import <UMCore/UMAppLifecycleService.h>
|
||
|
#import <UMCore/UMUtilities.h>
|
||
|
|
||
|
@protocol EXSplashScreenUtilService
|
||
|
|
||
|
- (UIViewController *)currentViewController;
|
||
|
|
||
|
@end
|
||
|
|
||
|
@interface EXSplashScreenModule ()
|
||
|
|
||
|
@property (nonatomic, weak) UMModuleRegistry *moduleRegistry;
|
||
|
@property (nonatomic, weak) id<UMUtilitiesInterface> utilities;
|
||
|
|
||
|
@end
|
||
|
|
||
|
@implementation EXSplashScreenModule
|
||
|
|
||
|
UM_EXPORT_MODULE(ExpoSplashScreen);
|
||
|
|
||
|
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
|
||
|
{
|
||
|
_moduleRegistry = moduleRegistry;
|
||
|
_utilities = [moduleRegistry getModuleImplementingProtocol:@protocol(UMUtilitiesInterface)];
|
||
|
[[moduleRegistry getModuleImplementingProtocol:@protocol(UMAppLifecycleService)] registerAppLifecycleListener:self];
|
||
|
}
|
||
|
|
||
|
UM_EXPORT_METHOD_AS(hideAsync,
|
||
|
hideWithResolve:(UMPromiseResolveBlock)resolve
|
||
|
reject:(UMPromiseRejectBlock)reject)
|
||
|
{
|
||
|
UM_WEAKIFY(self);
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
UM_ENSURE_STRONGIFY(self);
|
||
|
UIViewController *currentViewController = [self reactViewController];
|
||
|
[[self splashScreenService] hideSplashScreenFor:currentViewController
|
||
|
successCallback:^(BOOL hasEffect){ resolve(@(hasEffect)); }
|
||
|
failureCallback:^(NSString *message){ reject(@"ERR_SPLASH_SCREEN_CANNOT_HIDE", message, nil); }];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
UM_EXPORT_METHOD_AS(preventAutoHideAsync,
|
||
|
preventAutoHideWithResolve:(UMPromiseResolveBlock)resolve
|
||
|
reject:(UMPromiseRejectBlock)reject)
|
||
|
{
|
||
|
UM_WEAKIFY(self);
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
UM_ENSURE_STRONGIFY(self);
|
||
|
UIViewController *currentViewController = [self reactViewController];
|
||
|
[[self splashScreenService] preventSplashScreenAutoHideFor:currentViewController
|
||
|
successCallback:^(BOOL hasEffect){ resolve(@(hasEffect)); }
|
||
|
failureCallback:^(NSString *message){ reject(@"ERR_SPLASH_SCREEN_CANNOT_PREVENT_AUTOHIDE", message, nil); }];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
# pragma mark - UMAppLifecycleListener
|
||
|
|
||
|
- (void)onAppBackgrounded {}
|
||
|
|
||
|
- (void)onAppForegrounded {}
|
||
|
|
||
|
- (void)onAppContentDidAppear
|
||
|
{
|
||
|
UM_WEAKIFY(self);
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
UM_ENSURE_STRONGIFY(self);
|
||
|
UIViewController* currentViewController = [self reactViewController];
|
||
|
[[self splashScreenService] onAppContentDidAppear:currentViewController];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
- (void)onAppContentWillReload
|
||
|
{
|
||
|
UM_WEAKIFY(self);
|
||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
UM_ENSURE_STRONGIFY(self);
|
||
|
UIViewController* currentViewController = [self reactViewController];
|
||
|
[[self splashScreenService] onAppContentWillReload:currentViewController];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
# pragma mark - internals
|
||
|
|
||
|
/**
|
||
|
* Tries to obtain singleton module that is registered as "SplashScreen".
|
||
|
* Silent agreement is that registered module conforms to "EXSplashScreenService" protocol.
|
||
|
*/
|
||
|
- (id<EXSplashScreenService>)splashScreenService
|
||
|
{
|
||
|
return [self.moduleRegistry getSingletonModuleForName:@"SplashScreen"];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tries to obtain a reference to the UIViewController for the main RCTRootView
|
||
|
* by iterating through all of the application's windows and their viewControllers
|
||
|
* until it finds one with a RCTRootView.
|
||
|
*/
|
||
|
- (UIViewController *)reactViewController
|
||
|
{
|
||
|
dispatch_assert_queue(dispatch_get_main_queue());
|
||
|
|
||
|
// first check to see if the host application has a module that provides the reference we want
|
||
|
// (this is the case in Expo Go and in the ExpoKit pod used in `expo build` apps)
|
||
|
id<EXSplashScreenUtilService> utilService = [_moduleRegistry getSingletonModuleForName:@"Util"];
|
||
|
if (utilService != nil) {
|
||
|
return [utilService currentViewController];
|
||
|
}
|
||
|
|
||
|
UIViewController *controller = [self viewControllerContainingRCTRootView];
|
||
|
if (!controller) {
|
||
|
// no RCTRootView was found, so just fall back to the key window's root view controller
|
||
|
return self.utilities.currentViewController;
|
||
|
}
|
||
|
|
||
|
UIViewController *presentedController = controller.presentedViewController;
|
||
|
while (presentedController && ![presentedController isBeingDismissed]) {
|
||
|
controller = presentedController;
|
||
|
presentedController = controller.presentedViewController;
|
||
|
}
|
||
|
return controller;
|
||
|
}
|
||
|
|
||
|
- (nullable UIViewController *)viewControllerContainingRCTRootView
|
||
|
{
|
||
|
NSArray<UIWindow *> *allWindows;
|
||
|
if (@available(iOS 13, *)) {
|
||
|
NSSet<UIScene *> *allWindowScenes = UIApplication.sharedApplication.connectedScenes;
|
||
|
NSMutableArray<UIWindow *> *allForegroundWindows = [NSMutableArray new];
|
||
|
for (UIScene *scene in allWindowScenes.allObjects) {
|
||
|
if ([scene isKindOfClass:[UIWindowScene class]] && scene.activationState == UISceneActivationStateForegroundActive) {
|
||
|
[allForegroundWindows addObjectsFromArray:((UIWindowScene *)scene).windows];
|
||
|
}
|
||
|
}
|
||
|
allWindows = allForegroundWindows;
|
||
|
} else {
|
||
|
allWindows = UIApplication.sharedApplication.windows;
|
||
|
}
|
||
|
|
||
|
for (UIWindow *window in allWindows) {
|
||
|
UIViewController *controller = window.rootViewController;
|
||
|
if ([controller.view isKindOfClass:[RCTRootView class]]) {
|
||
|
return controller;
|
||
|
}
|
||
|
UIViewController *presentedController = controller.presentedViewController;
|
||
|
while (presentedController && ![presentedController isBeingDismissed]) {
|
||
|
if ([presentedController.view isKindOfClass:[RCTRootView class]]) {
|
||
|
return presentedController;
|
||
|
}
|
||
|
controller = presentedController;
|
||
|
presentedController = controller.presentedViewController;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no RCTRootView was found
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
@end
|