guardia-messenger/node_modules/expo-splash-screen/ios/EXSplashScreen/EXSplashScreenService.m
DESKTOP-TKLFCPRython f29f525c77 refactor: 101.79.17.164 → zioinfo.co.kr 전체 도메인 변환 + Manager UI 배포
- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스)
- Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포
- 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 10:09:17 +09:00

212 lines
9.7 KiB
Objective-C

// Copyright © 2018 650 Industries. All rights reserved.
#import <EXSplashScreen/EXSplashScreenService.h>
#import <EXSplashScreen/EXSplashScreenViewNativeProvider.h>
#import <ExpoModulesCore/EXDefines.h>
static NSString * const kRootViewController = @"rootViewController";
static NSString * const kView = @"view";
@interface EXSplashScreenService ()
@property (nonatomic, strong) NSMapTable<UIViewController *, EXSplashScreenViewController *> *splashScreenControllers;
/**
* This module holds a reference to rootViewController acting as a flag to indicate KVO is enabled.
* When KVO is enabled, actually we are observing two targets and re-show splash screen if targets changed:
* - `keyWindow.rootViewController`: it is for expo-dev-client which replaced it in startup.
* - `rootViewController.rootView`: it is for expo-updates which replaced it in startup.
*
* If `rootViewController` is changed, we also need the old `rootViewController` to unregister rootView KVO.
* That's why we keep a weak reference here but not a boolean flag.
*/
@property (nonatomic, weak) UIViewController *observingRootViewController;
@end
@implementation EXSplashScreenService
EX_REGISTER_SINGLETON_MODULE(SplashScreen);
- (instancetype)init
{
if (self = [super init]) {
_splashScreenControllers = [NSMapTable weakToStrongObjectsMapTable];
}
return self;
}
- (void)showSplashScreenFor:(UIViewController *)viewController
options:(EXSplashScreenOptions)options
{
id<EXSplashScreenViewProvider> splashScreenViewProvider = [EXSplashScreenViewNativeProvider new];
return [self showSplashScreenFor:viewController
options:options
splashScreenViewProvider:splashScreenViewProvider
successCallback:^{}
failureCallback:^(NSString *message){ EXLogWarn(@"%@", message); }];
}
- (void)showSplashScreenFor:(UIViewController *)viewController
options:(EXSplashScreenOptions)options
splashScreenViewProvider:(id<EXSplashScreenViewProvider>)splashScreenViewProvider
successCallback:(void (^)(void))successCallback
failureCallback:(void (^)(NSString * _Nonnull))failureCallback
{
if ((options & EXSplashScreenForceShow) == 0 && [self.splashScreenControllers objectForKey:viewController]) {
return failureCallback(@"'SplashScreen.show' has already been called for given view controller.");
}
UIView *rootView = viewController.view;
UIView *splashScreenView = [splashScreenViewProvider createSplashScreenView];
EXSplashScreenViewController *splashScreenController = [[EXSplashScreenViewController alloc] initWithRootView:rootView
splashScreenView:splashScreenView];
[self showSplashScreenFor:viewController
options:options
splashScreenController:splashScreenController
successCallback:successCallback
failureCallback:failureCallback];
}
- (void)showSplashScreenFor:(UIViewController *)viewController
options:(EXSplashScreenOptions)options
splashScreenController:(EXSplashScreenViewController *)splashScreenController
successCallback:(void (^)(void))successCallback
failureCallback:(void (^)(NSString * _Nonnull))failureCallback
{
if ((options & EXSplashScreenForceShow) == 0 && [self.splashScreenControllers objectForKey:viewController]) {
return failureCallback(@"'SplashScreen.show' has already been called for given view controller.");
}
[self.splashScreenControllers setObject:splashScreenController forKey:viewController];
[[self.splashScreenControllers objectForKey:viewController] showWithCallback:successCallback
failureCallback:failureCallback];
}
- (void)preventSplashScreenAutoHideFor:(UIViewController *)viewController
options:(EXSplashScreenOptions)options
successCallback:(void (^)(BOOL hasEffect))successCallback
failureCallback:(void (^)(NSString * _Nonnull))failureCallback
{
if (![self.splashScreenControllers objectForKey:viewController]) {
return failureCallback(@"No native splash screen registered for given view controller. Call 'SplashScreen.show' for given view controller first.");
}
return [[self.splashScreenControllers objectForKey:viewController] preventAutoHideWithCallback:successCallback
failureCallback:failureCallback];
}
- (void)hideSplashScreenFor:(UIViewController *)viewController
options:(EXSplashScreenOptions)options
successCallback:(void (^)(BOOL hasEffect))successCallback
failureCallback:(void (^)(NSString * _Nonnull))failureCallback
{
if (![self.splashScreenControllers objectForKey:viewController]) {
return failureCallback(@"No native splash screen registered for given view controller. Call 'SplashScreen.show' for given view controller first.");
}
[self removeRootViewControllerListener];
return [[self.splashScreenControllers objectForKey:viewController] hideWithCallback:successCallback
failureCallback:failureCallback];
}
- (void)onAppContentDidAppear:(UIViewController *)viewController
{
BOOL needsHide = [[self.splashScreenControllers objectForKey:viewController] needsHideOnAppContentDidAppear];
if (needsHide) {
[self hideSplashScreenFor:viewController
options:EXSplashScreenDefault
successCallback:^(BOOL hasEffect){}
failureCallback:^(NSString *message){}];
}
}
- (void)onAppContentWillReload:(UIViewController *)viewController
{
BOOL needsShow = [[self.splashScreenControllers objectForKey:viewController] needsShowOnAppContentWillReload];
if (needsShow) {
// For reloading apps, specify `EXSplashScreenForceShow` to show splash screen again
[self showSplashScreenFor:viewController
options:EXSplashScreenForceShow
splashScreenController:[self.splashScreenControllers objectForKey:viewController]
successCallback:^{}
failureCallback:^(NSString *message){}];
}
}
- (BOOL)isAppActive {
return UIApplication.sharedApplication.applicationState == UIApplicationStateActive;
}
# pragma mark - UIApplicationDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *rootViewController = [[application keyWindow] rootViewController];
if (rootViewController) {
[self showSplashScreenFor:rootViewController options:EXSplashScreenDefault];
}
[self addRootViewControllerListener];
return YES;
}
# pragma mark - RootViewController KVO
- (void)addRootViewControllerListener
{
NSAssert([NSThread isMainThread], @"Method must be called on main thread");
if (self.observingRootViewController == nil) {
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
[UIApplication.sharedApplication.keyWindow addObserver:self
forKeyPath:kRootViewController
options:NSKeyValueObservingOptionNew
context:nil];
[rootViewController addObserver:self forKeyPath:kView options:NSKeyValueObservingOptionNew context:nil];
self.observingRootViewController = rootViewController;
}
}
- (void)removeRootViewControllerListener
{
NSAssert([NSThread isMainThread], @"Method must be called on main thread");
if (self.observingRootViewController != nil) {
UIWindow *window = self.observingRootViewController.view.window;
[window removeObserver:self forKeyPath:kRootViewController context:nil];
[self.observingRootViewController removeObserver:self forKeyPath:kView context:nil];
self.observingRootViewController = nil;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if (object == UIApplication.sharedApplication.keyWindow && [keyPath isEqualToString:kRootViewController]) {
UIViewController *newRootViewController = change[@"new"];
// For unknown reasons, this function may be sometimes called twice with the same changes.
// What leads to warnings like this one: `'SplashScreen.show' has already been called for given view controller`.
// To prevent this weird behaviour, we check if the value was really changed.
if (newRootViewController != nil && self.observingRootViewController != nil && newRootViewController != self.observingRootViewController) {
[self removeRootViewControllerListener];
[self showSplashScreenFor:newRootViewController options:EXSplashScreenDefault];
[self addRootViewControllerListener];
}
}
if (object == UIApplication.sharedApplication.keyWindow.rootViewController && [keyPath isEqualToString:kView]) {
UIView *newView = change[@"new"];
if (newView != nil && [newView.nextResponder isKindOfClass:[UIViewController class]]) {
UIViewController *viewController = (UIViewController *)newView.nextResponder;
// To show splash screen as soon as possible, we do not wait for hiding callback and call showSplashScreen immediately.
// GCD main queue should keep the calls in sequence.
[self hideSplashScreenFor:viewController options:EXSplashScreenDefault successCallback:^(BOOL hasEffect){} failureCallback:^(NSString *message){}];
[self.splashScreenControllers removeObjectForKey:viewController];
[self showSplashScreenFor:viewController options:EXSplashScreenDefault];
}
}
}
@end