@@ -31,6 +31,11 @@ typedef BOOL (*GULRealOpenURLSourceApplicationAnnotationIMP)(
31
31
typedef BOOL (*GULRealOpenURLOptionsIMP)(
32
32
id , SEL , GULApplication *, NSURL *, NSDictionary <NSString *, id > *);
33
33
34
+ #if UISCENE_SUPPORTED
35
+ API_AVAILABLE (ios(13.0 ), tvos(13.0 ))
36
+ typedef void (*GULOpenURLContextsIMP)(id , SEL , UIScene *, NSSet <UIOpenURLContext *> *);
37
+ #endif // UISCENE_SUPPORTED
38
+
34
39
#pragma clang diagnostic push
35
40
#pragma clang diagnostic ignored "-Wstrict-prototypes"
36
41
typedef void (*GULRealHandleEventsForBackgroundURLSessionIMP)(
@@ -289,6 +294,20 @@ + (void)proxyOriginalDelegate {
289
294
id <GULApplicationDelegate> originalDelegate =
290
295
[GULAppDelegateSwizzler sharedApplication ].delegate ;
291
296
[GULAppDelegateSwizzler proxyAppDelegate: originalDelegate];
297
+
298
+ #if UISCENE_SUPPORTED
299
+ if (@available (iOS 13.0 , tvOS 13.0 , *)) {
300
+ if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled ]) {
301
+ return ;
302
+ } else {
303
+ [[NSNotificationCenter defaultCenter ]
304
+ addObserver: self
305
+ selector: @selector (handleSceneWillConnectToNotification: )
306
+ name: UISceneWillConnectNotification
307
+ object: nil ];
308
+ }
309
+ }
310
+ #endif // UISCENE_SUPPORTED
292
311
});
293
312
}
294
313
@@ -682,6 +701,17 @@ + (void)notifyInterceptorsWithMethodSelector:(SEL)methodSelector
682
701
}];
683
702
}
684
703
704
+ #if UISCENE_SUPPORTED
705
+ + (void )handleSceneWillConnectToNotification : (NSNotification *)notification {
706
+ if (@available (iOS 13.0 , tvOS 13.0 , *)) {
707
+ if ([notification.object isKindOfClass: [UIScene class ]]) {
708
+ UIScene *scene = (UIScene *)notification.object ;
709
+ [GULAppDelegateSwizzler proxySceneDelegateIfNeeded: scene];
710
+ }
711
+ }
712
+ }
713
+ #endif // UISCENE_SUPPORTED
714
+
685
715
// The methods below are donor methods which are added to the dynamic subclass of the App Delegate.
686
716
// They are called within the scope of the real App Delegate so |self| does not refer to the
687
717
// GULAppDelegateSwizzler instance but the real App Delegate instance.
@@ -761,6 +791,36 @@ - (BOOL)application:(GULApplication *)application
761
791
762
792
#endif // TARGET_OS_IOS
763
793
794
+ #pragma mark - [Donor Methods] UISceneDelegate URL handler
795
+
796
+ #if UISCENE_SUPPORTED
797
+ - (void )scene : (UIScene *)scene
798
+ openURLContexts : (NSSet <UIOpenURLContext *> *)URLContexts API_AVAILABLE(ios(13.0 ), tvos(13.0 )) {
799
+ if (@available (iOS 13.0 , tvOS 13.0 , *)) {
800
+ SEL methodSelector = @selector (scene:openURLContexts: );
801
+ // Call the real implementation if the real App Delegate has any.
802
+ NSValue *openURLContextsIMPPointer =
803
+ [GULAppDelegateSwizzler originalImplementationForSelector: methodSelector object: self ];
804
+ GULOpenURLContextsIMP openURLContextsIMP = [openURLContextsIMPPointer pointerValue ];
805
+
806
+ [GULAppDelegateSwizzler
807
+ notifyInterceptorsWithMethodSelector: methodSelector
808
+ callback: ^(id <GULApplicationDelegate> interceptor) {
809
+ if ([interceptor
810
+ conformsToProtocol: @protocol (UISceneDelegate)]) {
811
+ id <UISceneDelegate> sceneInterceptor =
812
+ (id <UISceneDelegate>)interceptor;
813
+ [sceneInterceptor scene: scene openURLContexts: URLContexts];
814
+ }
815
+ }];
816
+
817
+ if (openURLContextsIMP) {
818
+ openURLContextsIMP (self, methodSelector, scene, URLContexts);
819
+ }
820
+ }
821
+ }
822
+ #endif // UISCENE_SUPPORTED
823
+
764
824
#pragma mark - [Donor Methods] Network overridden handler methods
765
825
766
826
#if TARGET_OS_IOS || TARGET_OS_TV
@@ -1002,6 +1062,97 @@ + (void)proxyAppDelegate:(id<GULApplicationDelegate>)appDelegate {
1002
1062
}
1003
1063
}
1004
1064
1065
+ #if UISCENE_SUPPORTED
1066
+ + (void )proxySceneDelegateIfNeeded : (UIScene *)scene {
1067
+ Class realClass = [scene.delegate class ];
1068
+ NSString *className = NSStringFromClass (realClass);
1069
+
1070
+ // Skip proxying if the class has a prefix of kGULAppDelegatePrefix, which means it has been
1071
+ // proxied before.
1072
+ if ([className hasPrefix: kGULAppDelegatePrefix ]) {
1073
+ return ;
1074
+ }
1075
+
1076
+ NSString *classNameWithPrefix = [kGULAppDelegatePrefix stringByAppendingString: className];
1077
+ NSString *newClassName =
1078
+ [NSString stringWithFormat: @" %@ -%@ " , classNameWithPrefix, [NSUUID UUID ].UUIDString];
1079
+
1080
+ if (NSClassFromString (newClassName)) {
1081
+ GULLogError (
1082
+ kGULLoggerSwizzler , NO ,
1083
+ [NSString
1084
+ stringWithFormat: @" I-SWZ%06ld " ,
1085
+ (long )kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidSceneDelegate ],
1086
+ @" Cannot create a proxy for Scene Delegate. Subclass already exists. Original Class"
1087
+ @" : %@ , subclass: %@ " ,
1088
+ className, newClassName);
1089
+ return ;
1090
+ }
1091
+
1092
+ // Register the new class as subclass of the real one. Do not allocate more than the real class
1093
+ // size.
1094
+ Class sceneDelegateSubClass = objc_allocateClassPair (realClass, newClassName.UTF8String , 0 );
1095
+ if (sceneDelegateSubClass == Nil ) {
1096
+ GULLogError (
1097
+ kGULLoggerSwizzler , NO ,
1098
+ [NSString
1099
+ stringWithFormat: @" I-SWZ%06ld " ,
1100
+ (long )kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidSceneDelegate ],
1101
+ @" Cannot create a proxy for Scene Delegate. Subclass already exists. Original Class"
1102
+ @" : %@ , subclass: Nil" ,
1103
+ className);
1104
+ return ;
1105
+ }
1106
+
1107
+ NSMutableDictionary <NSString *, NSValue *> *realImplementationsBySelector =
1108
+ [[NSMutableDictionary alloc ] init ];
1109
+
1110
+ // For scene:openURLContexts:
1111
+ SEL openURLContextsSEL = @selector (scene:openURLContexts: );
1112
+ [self proxyDestinationSelector: openURLContextsSEL
1113
+ implementationsFromSourceSelector: openURLContextsSEL
1114
+ fromClass: [GULAppDelegateSwizzler class ]
1115
+ toClass: sceneDelegateSubClass
1116
+ realClass: realClass
1117
+ storeDestinationImplementationTo: realImplementationsBySelector];
1118
+
1119
+ // Store original implementations to a fake property of the original delegate.
1120
+ objc_setAssociatedObject (scene.delegate , &kGULRealIMPBySelectorKey ,
1121
+ [realImplementationsBySelector copy ], OBJC_ASSOCIATION_RETAIN_NONATOMIC );
1122
+ objc_setAssociatedObject (scene.delegate , &kGULRealClassKey , realClass,
1123
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC );
1124
+
1125
+ // The subclass size has to be exactly the same size with the original class size. The subclass
1126
+ // cannot have more ivars/properties than its superclass since it will cause an offset in memory
1127
+ // that can lead to overwriting the isa of an object in the next frame.
1128
+ if (class_getInstanceSize (realClass) != class_getInstanceSize (sceneDelegateSubClass)) {
1129
+ GULLogError (
1130
+ kGULLoggerSwizzler , NO ,
1131
+ [NSString
1132
+ stringWithFormat: @" I-SWZ%06ld " ,
1133
+ (long )kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidSceneDelegate ],
1134
+ @" Cannot create subclass of Scene Delegate, because the created subclass is not the "
1135
+ @" same size. %@ " ,
1136
+ className);
1137
+ NSAssert (NO , @" Classes must be the same size to swizzle isa" );
1138
+ return ;
1139
+ }
1140
+
1141
+ // Make the newly created class to be the subclass of the real Scene Delegate class.
1142
+ objc_registerClassPair (sceneDelegateSubClass);
1143
+ if (object_setClass (scene.delegate , sceneDelegateSubClass)) {
1144
+ GULLogDebug (
1145
+ kGULLoggerSwizzler , NO ,
1146
+ [NSString
1147
+ stringWithFormat: @" I-SWZ%06ld " ,
1148
+ (long )kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidSceneDelegate ],
1149
+ @" Successfully created Scene Delegate Proxy automatically. To disable the "
1150
+ @" proxy, set the flag %@ to NO (Boolean) in the Info.plist" ,
1151
+ [GULAppDelegateSwizzler correctAppDelegateProxyKey ]);
1152
+ }
1153
+ }
1154
+ #endif // UISCENE_SUPPORTED
1155
+
1005
1156
#pragma mark - Methods to print correct debug logs
1006
1157
1007
1158
+ (NSString *)correctAppDelegateProxyKey {
0 commit comments