#import "UnityAppController.h" 
 | 
#import "UnityAppController+ViewHandling.h" 
 | 
#import "UnityAppController+Rendering.h" 
 | 
#import "iPhone_Sensors.h" 
 | 
  
 | 
#import <CoreGraphics/CoreGraphics.h> 
 | 
#import <QuartzCore/QuartzCore.h> 
 | 
#import <QuartzCore/CADisplayLink.h> 
 | 
#import <Availability.h> 
 | 
  
 | 
#import <OpenGLES/EAGL.h> 
 | 
#import <OpenGLES/EAGLDrawable.h> 
 | 
#import <OpenGLES/ES2/gl.h> 
 | 
#import <OpenGLES/ES2/glext.h> 
 | 
  
 | 
#include <mach/mach_time.h> 
 | 
  
 | 
// MSAA_DEFAULT_SAMPLE_COUNT was moved to iPhone_GlesSupport.h 
 | 
// ENABLE_INTERNAL_PROFILER and related defines were moved to iPhone_Profiler.h 
 | 
// kFPS define for removed: you can use Application.targetFrameRate (30 fps by default) 
 | 
// DisplayLink is the only run loop mode now - all others were removed 
 | 
  
 | 
#include "CrashReporter.h" 
 | 
  
 | 
#include "UI/OrientationSupport.h" 
 | 
#include "UI/UnityView.h" 
 | 
#include "UI/Keyboard.h" 
 | 
#include "UI/SplashScreen.h" 
 | 
#include "Unity/InternalProfiler.h" 
 | 
#include "Unity/DisplayManager.h" 
 | 
#include "Unity/EAGLContextHelper.h" 
 | 
#include "Unity/GlesHelper.h" 
 | 
#include "Unity/ObjCRuntime.h" 
 | 
#include "PluginBase/AppDelegateListener.h" 
 | 
#include "UniversalSDK.h" 
 | 
  
 | 
#import <SMPCQuickSDK/SMPCQuickSDK.h> 
 | 
  
 | 
  
 | 
#include <objc/objc.h> 
 | 
#include <objc/runtime.h> 
 | 
  
 | 
// Set this to 1 to force single threaded rendering 
 | 
#define UNITY_FORCE_DIRECT_RENDERING 0 
 | 
  
 | 
// Standard Gesture Recognizers enabled on all iOS apps absorb touches close to the top and bottom of the screen. 
 | 
// This sometimes causes an ~1 second delay before the touch is handled when clicking very close to the edge. 
 | 
// You should enable this if you want to avoid that delay. Enabling it should not have any effect on default iOS gestures. 
 | 
#define DISABLE_TOUCH_DELAYS 1 
 | 
  
 | 
bool    _ios42orNewer           = false; 
 | 
bool    _ios43orNewer           = false; 
 | 
bool    _ios50orNewer           = false; 
 | 
bool    _ios60orNewer           = false; 
 | 
bool    _ios70orNewer           = false; 
 | 
bool    _ios80orNewer           = false; 
 | 
bool    _ios81orNewer           = false; 
 | 
bool    _ios82orNewer           = false; 
 | 
bool    _ios83orNewer           = false; 
 | 
bool    _ios90orNewer           = false; 
 | 
bool    _ios91orNewer           = false; 
 | 
bool    _ios100orNewer          = false; 
 | 
  
 | 
// was unity rendering already inited: we should not touch rendering while this is false 
 | 
bool    _renderingInited        = false; 
 | 
// was unity inited: we should not touch unity api while this is false 
 | 
bool    _unityAppReady          = false; 
 | 
// see if there's a need to do internal player pause/resume handling 
 | 
// 
 | 
// Typically the trampoline code should manage this internally, but 
 | 
// there are use cases, videoplayer, plugin code, etc where the player 
 | 
// is paused before the internal handling comes relevant. Avoid 
 | 
// overriding externally managed player pause/resume handling by 
 | 
// caching the state 
 | 
bool    _wasPausedExternal      = false; 
 | 
// should we skip present on next draw: used in corner cases (like rotation) to fill both draw-buffers with some content 
 | 
bool    _skipPresent            = false; 
 | 
// was app "resigned active": some operations do not make sense while app is in background 
 | 
bool    _didResignActive        = false; 
 | 
  
 | 
// was startUnity scheduled: used to make startup robust in case of locking device 
 | 
static bool _startUnityScheduled    = false; 
 | 
  
 | 
bool    _supportsMSAA           = false; 
 | 
  
 | 
  
 | 
@implementation UnityAppController 
 | 
  
 | 
@synthesize unityView               = _unityView; 
 | 
@synthesize unityDisplayLink        = _unityDisplayLink; 
 | 
  
 | 
@synthesize rootView                = _rootView; 
 | 
@synthesize rootViewController      = _rootController; 
 | 
@synthesize mainDisplay             = _mainDisplay; 
 | 
@synthesize renderDelegate          = _renderDelegate; 
 | 
@synthesize quitHandler             = _quitHandler; 
 | 
  
 | 
#if !UNITY_TVOS 
 | 
@synthesize interfaceOrientation    = _curOrientation; 
 | 
#endif 
 | 
  
 | 
- (id)init 
 | 
{ 
 | 
    if ((self = [super init])) 
 | 
    { 
 | 
        // due to clang issues with generating warning for overriding deprecated methods 
 | 
        // we will simply assert if deprecated methods are present 
 | 
        // NB: methods table is initied at load (before this call), so it is ok to check for override 
 | 
        NSAssert(![self respondsToSelector: @selector(createUnityViewImpl)], 
 | 
            @"createUnityViewImpl is deprecated and will not be called. Override createUnityView" 
 | 
            ); 
 | 
        NSAssert(![self respondsToSelector: @selector(createViewHierarchyImpl)], 
 | 
            @"createViewHierarchyImpl is deprecated and will not be called. Override willStartWithViewController" 
 | 
            ); 
 | 
        NSAssert(![self respondsToSelector: @selector(createViewHierarchy)], 
 | 
            @"createViewHierarchy is deprecated and will not be implemented. Use createUI" 
 | 
            ); 
 | 
    } 
 | 
    return self; 
 | 
} 
 | 
  
 | 
- (void)setWindow:(id)object        {} 
 | 
- (UIWindow*)window                 { return _window; } 
 | 
  
 | 
  
 | 
- (void)shouldAttachRenderDelegate  {} 
 | 
- (void)preStartUnity               {} 
 | 
  
 | 
  
 | 
- (void)startUnity:(UIApplication*)application 
 | 
{ 
 | 
    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized"); 
 | 
  
 | 
    UnityInitApplicationGraphics(UNITY_FORCE_DIRECT_RENDERING); 
 | 
  
 | 
    // we make sure that first level gets correct display list and orientation 
 | 
    [[DisplayManager Instance] updateDisplayListInUnity]; 
 | 
  
 | 
    UnityLoadApplication(); 
 | 
    Profiler_InitProfiler(); 
 | 
  
 | 
    [self showGameUI]; 
 | 
    [self createDisplayLink]; 
 | 
  
 | 
    UnitySetPlayerFocus(1); 
 | 
} 
 | 
  
 | 
extern "C" void UnityDestroyDisplayLink() 
 | 
{ 
 | 
    [GetAppController() destroyDisplayLink]; 
 | 
} 
 | 
  
 | 
extern "C" void UnityRequestQuit() 
 | 
{ 
 | 
    _didResignActive = true; 
 | 
    if (GetAppController().quitHandler) 
 | 
        GetAppController().quitHandler(); 
 | 
    else 
 | 
        exit(0); 
 | 
} 
 | 
  
 | 
#if !UNITY_TVOS 
 | 
- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window 
 | 
{ 
 | 
    // UIInterfaceOrientationMaskAll 
 | 
    // it is the safest way of doing it: 
 | 
    // - GameCenter and some other services might have portrait-only variant 
 | 
    //     and will throw exception if portrait is not supported here 
 | 
    // - When you change allowed orientations if you end up forbidding current one 
 | 
    //     exception will be thrown 
 | 
    // Anyway this is intersected with values provided from UIViewController, so we are good 
 | 
     
 | 
    [[SMPCQuickSDK defaultInstance] application:application supportedInterfaceOrientationsForWindow:window]; 
 | 
     
 | 
    return (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationPortraitUpsideDown) 
 | 
        | (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationLandscapeLeft); 
 | 
} 
 | 
  
 | 
#endif 
 | 
  
 | 
#if !UNITY_TVOS 
 | 
- (void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification 
 | 
{ 
 | 
    AppController_SendNotificationWithArg(kUnityDidReceiveLocalNotification, notification); 
 | 
    UnitySendLocalNotification(notification); 
 | 
} 
 | 
  
 | 
#endif 
 | 
  
 | 
#if UNITY_USES_REMOTE_NOTIFICATIONS 
 | 
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo 
 | 
{ 
 | 
    AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo); 
 | 
    UnitySendRemoteNotification(userInfo); 
 | 
    [[SMPCQuickSDK defaultInstance] application:application didReceiveRemoteNotification:userInfo]; 
 | 
} 
 | 
  
 | 
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken 
 | 
{ 
 | 
    AppController_SendNotificationWithArg(kUnityDidRegisterForRemoteNotificationsWithDeviceToken, deviceToken); 
 | 
    UnitySendDeviceToken(deviceToken); 
 | 
    [[SMPCQuickSDK defaultInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 
 | 
} 
 | 
  
 | 
#if !UNITY_TVOS 
 | 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler 
 | 
{ 
 | 
    AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo); 
 | 
    UnitySendRemoteNotification(userInfo); 
 | 
  
 | 
    if (handler) 
 | 
    { 
 | 
        handler(UIBackgroundFetchResultNoData); 
 | 
    } 
 | 
} 
 | 
  
 | 
#endif 
 | 
  
 | 
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error 
 | 
{ 
 | 
    AppController_SendNotificationWithArg(kUnityDidFailToRegisterForRemoteNotificationsWithError, error); 
 | 
    UnitySendRemoteNotificationError(error); 
 | 
    [[SMPCQuickSDK defaultInstance] application:application didFailToRegisterForRemoteNotificationsWithError:error]; 
 | 
} 
 | 
  
 | 
#endif 
 | 
  
 | 
- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation 
 | 
{ 
 | 
    [[SMPCQuickSDK defaultInstance] openURL:url sourceApplication:sourceApplication application:application annotation:annotation]; 
 | 
    NSMutableArray* keys    = [NSMutableArray arrayWithCapacity: 3]; 
 | 
    NSMutableArray* values  = [NSMutableArray arrayWithCapacity: 3]; 
 | 
  
 | 
    #define ADD_ITEM(item)  do{ if(item) {[keys addObject:@#item]; [values addObject:item];} }while(0) 
 | 
  
 | 
    ADD_ITEM(url); 
 | 
    ADD_ITEM(sourceApplication); 
 | 
    ADD_ITEM(annotation); 
 | 
  
 | 
    #undef ADD_ITEM 
 | 
  
 | 
    NSDictionary* notifData = [NSDictionary dictionaryWithObjects: values forKeys: keys]; 
 | 
    AppController_SendNotificationWithArg(kUnityOnOpenURL, notifData); 
 | 
    return YES; 
 | 
} 
 | 
  
 | 
- (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions 
 | 
{ 
 | 
    return YES; 
 | 
} 
 | 
  
 | 
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions 
 | 
{ 
 | 
    ::printf("-> applicationDidFinishLaunching()\n"); 
 | 
  
 | 
    // send notfications 
 | 
#if !UNITY_TVOS 
 | 
    if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey]) 
 | 
        UnitySendLocalNotification(notification); 
 | 
  
 | 
    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO) 
 | 
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 
 | 
#endif 
 | 
  
 | 
    UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]); 
 | 
  
 | 
    [self selectRenderingAPI]; 
 | 
    [UnityRenderingView InitializeForAPI: self.renderingAPI]; 
 | 
  
 | 
        CGRect _winSize = [UIScreen mainScreen].bounds; 
 | 
    if(KIsiPhoneX){ 
 | 
        _winSize.origin.x = 40; 
 | 
        _winSize.size.width -= 92; 
 | 
        _winSize.size.height -= 15; 
 | 
    } 
 | 
    _window = [[UIWindow alloc] initWithFrame:_winSize]; 
 | 
  
 | 
    _unityView      = [self createUnityView]; 
 | 
  
 | 
    [DisplayManager Initialize]; 
 | 
    _mainDisplay    = [DisplayManager Instance].mainDisplay; 
 | 
    [_mainDisplay createWithWindow: _window andView: _unityView]; 
 | 
  
 | 
    [self createUI]; 
 | 
    [self preStartUnity]; 
 | 
  
 | 
    // if you wont use keyboard you may comment it out at save some memory 
 | 
    [KeyboardDelegate Initialize]; 
 | 
  
 | 
    _universalSDK = [[UniversalSDK alloc] init]; 
 | 
    [_universalSDK QuickSDKInit:application didFinishLaunchingWithOptions:launchOptions]; 
 | 
  
 | 
  
 | 
#if !PLATFORM_TVOS && DISABLE_TOUCH_DELAYS 
 | 
    for (UIGestureRecognizer *g in _window.gestureRecognizers) 
 | 
    { 
 | 
        g.delaysTouchesBegan = false; 
 | 
    } 
 | 
#endif 
 | 
  
 | 
    return YES; 
 | 
} 
 | 
  
 | 
- (void)applicationDidEnterBackground:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("-> applicationDidEnterBackground()\n"); 
 | 
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; 
 | 
    [[SMPCQuickSDK defaultInstance] applicationDidEnterBackground:application]; 
 | 
} 
 | 
  
 | 
- (void)applicationWillEnterForeground:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("-> applicationWillEnterForeground()\n"); 
 | 
    [application setApplicationIconBadgeNumber:0]; 
 | 
    [application cancelAllLocalNotifications]; 
 | 
    [[SMPCQuickSDK defaultInstance] applicationWillEnterForeground:application]; 
 | 
  
 | 
    // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup) 
 | 
    if (_unityAppReady) 
 | 
    { 
 | 
        // if we were showing video before going to background - the view size may be changed while we are in background 
 | 
        [GetAppController().unityView recreateGLESSurfaceIfNeeded]; 
 | 
    } 
 | 
} 
 | 
  
 | 
- (void)applicationDidBecomeActive:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("-> applicationDidBecomeActive()\n"); 
 | 
  
 | 
    [self removeSnapshotView]; 
 | 
  
 | 
    if (_unityAppReady) 
 | 
    { 
 | 
        if (UnityIsPaused() && _wasPausedExternal == false) 
 | 
        { 
 | 
            UnityWillResume(); 
 | 
            UnityPause(0); 
 | 
        } 
 | 
        if (_wasPausedExternal) 
 | 
        { 
 | 
            if (UnityIsFullScreenPlaying()) 
 | 
                TryResumeFullScreenVideo(); 
 | 
        } 
 | 
        UnitySetPlayerFocus(1); 
 | 
    } 
 | 
    else if (!_startUnityScheduled) 
 | 
    { 
 | 
        _startUnityScheduled = true; 
 | 
        [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0]; 
 | 
    } 
 | 
  
 | 
    _didResignActive = false; 
 | 
    [[SMPCQuickSDK defaultInstance] applicationDidBecomeActive:application]; 
 | 
} 
 | 
  
 | 
- (void)removeSnapshotView 
 | 
{ 
 | 
    // do this on the main queue async so that if we try to create one 
 | 
    // and remove in the same frame, this always happens after in the same queue 
 | 
    dispatch_async(dispatch_get_main_queue(), ^{ 
 | 
        if (_snapshotView) 
 | 
        { 
 | 
            [_snapshotView removeFromSuperview]; 
 | 
            _snapshotView = nil; 
 | 
        } 
 | 
    }); 
 | 
} 
 | 
  
 | 
- (void)applicationWillResignActive:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("-> applicationWillResignActive()\n"); 
 | 
  
 | 
    if (_unityAppReady) 
 | 
    { 
 | 
        UnitySetPlayerFocus(0); 
 | 
  
 | 
        _wasPausedExternal = UnityIsPaused(); 
 | 
        if (_wasPausedExternal == false) 
 | 
        { 
 | 
            // do pause unity only if we dont need special background processing 
 | 
            // otherwise batched player loop can be called to run user scripts 
 | 
            int bgBehavior = UnityGetAppBackgroundBehavior(); 
 | 
            if (bgBehavior == appbgSuspend || bgBehavior == appbgExit) 
 | 
            { 
 | 
                // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager. 
 | 
                // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop) 
 | 
                // NB: We will actually pause after the loop (when calling UnityPause). 
 | 
                UnityWillPause(); 
 | 
                [self repaint]; 
 | 
                UnityPause(1); 
 | 
  
 | 
                // this is done on the next frame so that 
 | 
                // in the case where unity is paused while going 
 | 
                // into the background and an input is deactivated 
 | 
                // we don't mess with the view hierarchy while taking 
 | 
                // a view snapshot (case 760747). 
 | 
                dispatch_async(dispatch_get_main_queue(), ^{ 
 | 
                    // if we are active again, we don't need to do this anymore 
 | 
                    if (!_didResignActive) 
 | 
                    { 
 | 
                        return; 
 | 
                    } 
 | 
  
 | 
                    _snapshotView = [self createSnapshotView]; 
 | 
                    if (_snapshotView) 
 | 
                        [_rootView addSubview: _snapshotView]; 
 | 
                }); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    _didResignActive = true; 
 | 
     
 | 
    [[SMPCQuickSDK defaultInstance] applicationWillResignActive:application]; 
 | 
} 
 | 
  
 | 
- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("WARNING -> applicationDidReceiveMemoryWarning()\n"); 
 | 
    UnityLowMemory(); 
 | 
} 
 | 
  
 | 
- (void)applicationWillTerminate:(UIApplication*)application 
 | 
{ 
 | 
    ::printf("-> applicationWillTerminate()\n"); 
 | 
    [[SMPCQuickSDK defaultInstance] applicationWillTerminate:application]; 
 | 
     
 | 
    Profiler_UninitProfiler(); 
 | 
    UnityCleanup(); 
 | 
  
 | 
    extern void SensorsCleanup(); 
 | 
    SensorsCleanup(); 
 | 
} 
 | 
  
 | 
@end 
 | 
  
 | 
  
 | 
void AppController_SendNotification(NSString* name) 
 | 
{ 
 | 
    [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController()]; 
 | 
} 
 | 
  
 | 
void AppController_SendNotificationWithArg(NSString* name, id arg) 
 | 
{ 
 | 
    [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg]; 
 | 
} 
 | 
  
 | 
void AppController_SendUnityViewControllerNotification(NSString* name) 
 | 
{ 
 | 
    [[NSNotificationCenter defaultCenter] postNotificationName: name object: UnityGetGLViewController()]; 
 | 
} 
 | 
  
 | 
extern "C" UIWindow*            UnityGetMainWindow()        { 
 | 
    return GetAppController().mainDisplay.window; 
 | 
} 
 | 
extern "C" UIViewController*    UnityGetGLViewController()  { 
 | 
    return GetAppController().rootViewController; 
 | 
} 
 | 
extern "C" UIView*              UnityGetGLView()            { 
 | 
    return GetAppController().unityView; 
 | 
} 
 | 
extern "C" ScreenOrientation    UnityCurrentOrientation()   { return GetAppController().unityView.contentOrientation; } 
 | 
  
 | 
extern "C" void IOSMessageHandle(const char* jsonString) { 
 | 
    [GetAppController().universalSDK HandleUnityMessage:[NSString stringWithUTF8String:jsonString]]; 
 | 
} 
 | 
  
 | 
  
 | 
  
 | 
bool LogToNSLogHandler(LogType logType, const char* log, va_list list) 
 | 
{ 
 | 
    NSLogv([NSString stringWithUTF8String: log], list); 
 | 
    return true; 
 | 
} 
 | 
  
 | 
void UnityInitTrampoline() 
 | 
{ 
 | 
#if ENABLE_CRASH_REPORT_SUBMISSION 
 | 
    SubmitCrashReportsAsync(); 
 | 
#endif 
 | 
    InitCrashHandling(); 
 | 
  
 | 
    NSString* version = [[UIDevice currentDevice] systemVersion]; 
 | 
  
 | 
    // keep native plugin developers happy and keep old bools around 
 | 
    _ios42orNewer = true; 
 | 
    _ios43orNewer = true; 
 | 
    _ios50orNewer = true; 
 | 
    _ios60orNewer = true; 
 | 
    _ios70orNewer = [version compare: @"7.0" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios80orNewer = [version compare: @"8.0" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios81orNewer = [version compare: @"8.1" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios82orNewer = [version compare: @"8.2" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios83orNewer = [version compare: @"8.3" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios90orNewer = [version compare: @"9.0" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios91orNewer = [version compare: @"9.1" options: NSNumericSearch] != NSOrderedAscending; 
 | 
    _ios100orNewer = [version compare: @"10.0" options: NSNumericSearch] != NSOrderedAscending; 
 | 
  
 | 
    // Try writing to console and if it fails switch to NSLog logging 
 | 
    ::fprintf(stdout, "\n"); 
 | 
    if (::ftell(stdout) < 0) 
 | 
        UnitySetLogEntryHandler(LogToNSLogHandler); 
 | 
  
 | 
    if (![[UIView class] instancesRespondToSelector: @selector(safeAreaInsets)]) 
 | 
    { 
 | 
        IMP UIView_SafeAreaInsets_IMP = imp_implementationWithBlock(^UIEdgeInsets(id _self) { 
 | 
            return UIEdgeInsetsZero; 
 | 
        }); 
 | 
        class_replaceMethod([UIView class], @selector(safeAreaInsets), UIView_SafeAreaInsets_IMP, UIView_safeAreaInsets_Enc); 
 | 
    } 
 | 
} 
 |