1212#import < PlayTools/PlayTools-Swift.h>
1313#import " PTFakeMetaTouch.h"
1414
15+ __attribute__ ((visibility(" hidden" )))
16+ @interface PTSwizzleLoader : NSObject
17+ @end
18+
1519@implementation NSObject (Swizzle)
1620
1721- (void ) swizzleInstanceMethod:(SEL )origSelector withMethod:(SEL )newSelector
@@ -45,20 +49,6 @@ - (void) swizzleInstanceMethod:(SEL)origSelector withMethod:(SEL)newSelector
4549 }
4650}
4751
48- + (void ) load
49- {
50- // TODO: UINSview
51-
52- if ([[PlaySettings shared ] adaptiveDisplay ]) {
53- [objc_getClass (" FBSSceneSettings" ) swizzleInstanceMethod: @selector (frame ) withMethod: @selector (hook_frame )];
54- [objc_getClass (" FBSSceneSettings" ) swizzleInstanceMethod: @selector (bounds ) withMethod: @selector (hook_bounds )];
55- [objc_getClass (" FBSDisplayMode" ) swizzleInstanceMethod: @selector (size ) withMethod: @selector (hook_size )];
56- }
57-
58- [objc_getClass (" IOSViewController" ) swizzleInstanceMethod: @selector (prefersPointerLocked ) withMethod: @selector (hook_prefersPointerLocked )];
59- [self swizzleInstanceMethod: @selector (init ) withMethod: @selector (hook_init )];
60- }
61-
6252- (BOOL ) hook_prefersPointerLocked {
6353 return false ;
6454}
@@ -76,15 +66,51 @@ - (CGSize) hook_size {
7666}
7767
7868bool menuWasCreated = false ;
79-
80- -( id ) hook_init {
69+ - ( id ) initWithRootMenuHook:( id )rootMenu {
70+ self = [ self initWithRootMenuHook: rootMenu];
8171 if (!menuWasCreated) {
82- if ([[self class ] isEqual: NSClassFromString (@" _UIMenuBuilder" )]) {
83- [PlayCover initMenuWithMenu: self ];
84- menuWasCreated = TRUE ;
85- }
72+ [PlayCover initMenuWithMenu: self ];
73+ menuWasCreated = TRUE ;
8674 }
87-
8875 return self;
8976}
77+
78+ @end
79+
80+ /*
81+ This class only exists to apply swizzles from the +load of a class that won't have any categories/extensions. The reason
82+ for not doing this in a C module initializer is that obj-c initialization happens before any __attribute__((constructor))
83+ is called. This way we can guarantee the hooks will be applied before [PlayCover launch] is called (in PlayLoader.m).
84+
85+ Side note:
86+ While adding method replacements to NSObject does work, I'm not certain this doesn't (or won't) have any side effects. The
87+ way Apple does method swizzling internally is by creating a category of the swizzled class and adding the replacements there.
88+ This keeps all those replacements "local" to that class. Example:
89+
90+ '''
91+ @interface FBSSceneSettings (Swizzle)
92+ -(CGRect) hook_frame {
93+ ...
94+ }
95+ @end
96+
97+ Somewhere else:
98+ swizzle(FBSSceneSettings.class, @selector(frame), @selector(hook_frame);
99+ '''
100+
101+ However, doing this would require generating @interface declarations (either with class-dump or by hand) which would add a lot
102+ of code and complexity. I'm not sure this trade-off is "worth it", at least at the time of writing.
103+ */
104+ @implementation PTSwizzleLoader
105+ + (void )load {
106+ if ([[PlaySettings shared ] adaptiveDisplay ]) {
107+ [objc_getClass (" FBSSceneSettings" ) swizzleInstanceMethod: @selector (frame ) withMethod: @selector (hook_frame )];
108+ [objc_getClass (" FBSSceneSettings" ) swizzleInstanceMethod: @selector (bounds ) withMethod: @selector (hook_bounds )];
109+ [objc_getClass (" FBSDisplayMode" ) swizzleInstanceMethod: @selector (size ) withMethod: @selector (hook_size )];
110+ }
111+
112+ [objc_getClass (" _UIMenuBuilder" ) swizzleInstanceMethod: sel_getUid (" initWithRootMenu:" ) withMethod: @selector (initWithRootMenuHook: )];
113+ [objc_getClass (" IOSViewController" ) swizzleInstanceMethod: @selector (prefersPointerLocked ) withMethod: @selector (hook_prefersPointerLocked )];
114+ }
115+
90116@end
0 commit comments