├── preview.gif
├── fullscreenER.xcodeproj
├── project.xcworkspace
│ ├── xcuserdata
│ │ ├── w0lf.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ └── wolf.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── w0lf.xcuserdatad
│ │ ├── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── fullscreenER.xcscheme
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
└── project.pbxproj
├── fullscreenER
├── fullscreenER.h
├── Info.plist
├── fullscreenER.m
└── ZKSwizzle
│ ├── ZKSwizzle.h
│ └── ZKSwizzle.m
└── README.md
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w0lfschild/fullscreenER/HEAD/preview.gif
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/project.xcworkspace/xcuserdata/w0lf.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w0lfschild/fullscreenER/HEAD/fullscreenER.xcodeproj/project.xcworkspace/xcuserdata/w0lf.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/project.xcworkspace/xcuserdata/wolf.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w0lfschild/fullscreenER/HEAD/fullscreenER.xcodeproj/project.xcworkspace/xcuserdata/wolf.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/fullscreenER/fullscreenER.h:
--------------------------------------------------------------------------------
1 | //
2 | // fullscreenER.h
3 | // fullscreenER
4 | //
5 | // Created by Wolfgang Baird on 9/10/15.
6 | // Copyright © 2015 -2016 Wolfgang Baird. All rights reserved.
7 | //
8 |
9 | @import AppKit;
10 | #import "ZKSwizzle.h"
11 |
12 | @interface fullscreenER : NSObject
13 |
14 | + (void)load;
15 |
16 | @end
17 |
18 | @interface NSWindow (fullscreenER)
19 |
20 | - (void)setFrame:(struct CGRect)arg1 display:(BOOL)arg2 animate:(BOOL)arg3;
21 | - (struct CGRect)_tileFrameForFullScreen;
22 | - (struct CGRect)_frameForFullScreenMode;
23 | - (BOOL)_inFullScreen;
24 | - (void)setShowsLockButton:(BOOL)arg1;
25 | - (BOOL)showsLockButton;
26 |
27 | @end
28 |
29 | @interface fullscreenER_NSWindow : NSWindow
30 |
31 | @end
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/xcuserdata/w0lf.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | fullscreenER.xcscheme
8 |
9 | orderHint
10 | 1
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | FBB53C931D0BC99600DD182D
16 |
17 | primary
18 |
19 |
20 | FBFEDC1F1BA261DE009B4689
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/xcuserdata/w0lf.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fullscreenER
2 |
3 | 
4 |
5 | # Information:
6 |
7 | - Designed for 10.10+
8 | - MacForge plugin to improve the functionallity of the green maximize button on OS X
9 | - Author: [w0lfschild](https://github.com/w0lfschild)
10 |
11 | # Notes:
12 |
13 | - All application windows are resizable
14 | - Green maximize button functionality changes
15 | - Pressing will toggle between zooming to full size of screen and last frame size
16 | - Pressing while holding option toggles native fullscreen
17 | - Pressing while in fullscreen exits fullscreen
18 |
19 | # Installation:
20 |
21 | 1. Download and open [MacForge](https://github.com/w0lfschild/app_updates/raw/master/MacForge/MacForge.zip)
22 | 2. Install [fullscreenER](https://www.macenhance.com/mflink?macforge://github.com/w0lfschild/myRepo/raw/master/mytweaks/org.w0lf.fullscreenER)
23 | 3. Restart Chrome
24 |
25 | ### License:
26 | Pretty much the BSD license, just don't repackage it and call it your own please!
27 | Also if you do make some changes, feel free to make a pull request and help make things more awesome!
28 |
--------------------------------------------------------------------------------
/fullscreenER/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.2.4
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 11
23 | NSHumanReadableCopyright
24 | Copyright © 2016-2017 Wolfgang Baird. All rights reserved.
25 | NSPrincipalClass
26 |
27 | SIMBLTargetApplications
28 |
29 |
30 | BundleIdentifier
31 | *
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/xcuserdata/w0lf.xcuserdatad/xcschemes/fullscreenER.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/fullscreenER/fullscreenER.m:
--------------------------------------------------------------------------------
1 | //
2 | // fullscreenER.m
3 | // fullscreenER
4 | //
5 | // Created by Wolfgang on 9/13/15.
6 | // Copyright (c) 2015 - 2016 Wolfgang. All rights reserved.
7 | //
8 |
9 | #import "fullscreenER.h"
10 |
11 | #define APP_BLACKLIST @[@"com.apple.notificationcenterui"]
12 | #define CLS_BLACKLIST @[@"NSStatusBarWindow"]
13 |
14 | fullscreenER *plugin;
15 | BOOL _willMaximize = NO;
16 | NSInteger osx_ver;
17 | NSWindow *mykeyWindow;
18 | struct CGRect _currentFrame;
19 | static void *cachedFrame = &cachedFrame;
20 |
21 | @implementation fullscreenER
22 |
23 | + (fullscreenER*) sharedInstance {
24 | static fullscreenER* plugin = nil;
25 | if (plugin == nil)
26 | plugin = [[fullscreenER alloc] init];
27 | return plugin;
28 | }
29 |
30 | + (void)load {
31 | plugin = [fullscreenER sharedInstance];
32 | osx_ver = [[NSProcessInfo processInfo] operatingSystemVersion].minorVersion;
33 |
34 | if (osx_ver >= 9) {
35 | if (![APP_BLACKLIST containsObject:[[NSBundle mainBundle] bundleIdentifier]]) {
36 | ZKSwizzle(fullscreenER_NSWindow, NSWindow);
37 |
38 | for (NSWindow *win in [NSApp windows])
39 | [plugin FSER_initialize:win];
40 |
41 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
42 | NSMenu *subMenu = [NSApp windowsMenu];
43 | if (subMenu == nil)
44 | subMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
45 | [[subMenu addItemWithTitle:@"Toggle Fullscreen" action:@selector(FSER_toggleFS:) keyEquivalent:@""] setTarget:plugin];
46 | });
47 |
48 | mykeyWindow = [NSApp mainWindow];
49 |
50 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
51 | [center addObserver:self selector:@selector(FSER_gainFocus:) name:NSWindowDidBecomeMainNotification object:nil];
52 | [center addObserver:self selector:@selector(FSER_gainFocus:) name:NSWindowDidBecomeKeyNotification object:nil];
53 |
54 | NSLog(@"%@ loaded into %@ on macOS 10.%ld", [self class], [[NSBundle mainBundle] bundleIdentifier], (long)osx_ver);
55 | } else {
56 | NSLog(@"fullscreenER is blocked in this application because of issues");
57 | }
58 | } else {
59 | NSLog(@"fullscreenER is blocked in this application because of your version of macOS is too old");
60 | }
61 | }
62 |
63 | - (void)FSER_initialize:(NSWindow*)win
64 | {
65 | if (![CLS_BLACKLIST containsObject:[win className]])
66 | {
67 | Boolean editMask = ![[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@/QtCore.framework/Versions/4/QtCore", [[NSBundle mainBundle] privateFrameworksPath]]];
68 | if (editMask)
69 | win.styleMask = win.styleMask | NSResizableWindowMask;
70 | Boolean lockButton = [win showsLockButton];
71 | [win setShowsLockButton:!lockButton];
72 | [win setShowsLockButton:lockButton];
73 | }
74 | }
75 |
76 | + (void)FSER_gainFocus:(NSNotification *)note {
77 | mykeyWindow = [note object];
78 | }
79 |
80 | - (void)FSER_toggleFS:(id)sender {
81 | if ([mykeyWindow isVisible])
82 | [mykeyWindow toggleFullScreen:mykeyWindow];
83 | }
84 |
85 | @end
86 |
87 | @implementation fullscreenER_NSWindow
88 |
89 | - (BOOL)_allowsFullScreen {
90 | return YES;
91 | }
92 |
93 | - (BOOL)canEnterFullScreenMode {
94 | return YES;
95 | }
96 |
97 | - (BOOL)showsFullScreenButton {
98 | return YES;
99 | }
100 |
101 | - (BOOL)_canEnterFullScreenOrTileMode {
102 | return YES;
103 | }
104 |
105 | - (BOOL)_canEnterTileMode {
106 | return YES;
107 | }
108 |
109 | - (BOOL)_allowedInDashboardSpaceWithCollectionBehavior:(unsigned long long)arg1 {
110 | return YES;
111 | }
112 |
113 | - (BOOL)_allowedInOtherAppsFullScreenSpaceWithCollectionBehavior:(unsigned long long)arg1 {
114 | return YES;
115 | }
116 |
117 | // Full screen toggle
118 | - (void)wb_fullScreen {
119 | [self toggleFullScreen:self];
120 | }
121 |
122 | // Fill screen toggle
123 | - (void)wb_fillScreen {
124 | if ([self _inFullScreen]) {
125 | [self toggleFullScreen:self];
126 | } else {
127 | _currentFrame = self.frame;
128 | CGRect futureFrame = osx_ver < 11 ? [self _frameForFullScreenMode] : [self _tileFrameForFullScreen];
129 | CGRect screenFrame = self.screen.visibleFrame;
130 | Boolean isMaximized = CGRectEqualToRect(_currentFrame, screenFrame);
131 | if (!isMaximized) {
132 | NSValue *cachedFrameValue = [NSValue valueWithRect:(NSRect)NSRectFromCGRect(_currentFrame)];
133 | objc_setAssociatedObject(self, cachedFrame, cachedFrameValue, OBJC_ASSOCIATION_RETAIN);
134 | [self setFrame:futureFrame display:true animate:true];
135 | } else {
136 | NSValue *cachedValue = objc_getAssociatedObject(self, cachedFrame);
137 | CGRect cachedFrame = NSRectToCGRect(cachedValue.rectValue);
138 | [self setFrame:cachedFrame display:true animate:true];
139 | }
140 | }
141 | }
142 |
143 | - (BOOL)_zoomButtonIsFullScreenButton {
144 | if (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) {
145 | [[self standardWindowButton:NSWindowZoomButton] setAction:@selector(wb_fullScreen)];
146 | [[self standardWindowButton:NSWindowZoomButton] setAlphaValue:.5 ];
147 | } else {
148 | [[self standardWindowButton:NSWindowZoomButton] setAction:@selector(wb_fillScreen)];
149 | [[self standardWindowButton:NSWindowZoomButton] setAlphaValue:1 ];
150 | }
151 | return NO;
152 | }
153 |
154 | @end
155 |
--------------------------------------------------------------------------------
/fullscreenER/ZKSwizzle/ZKSwizzle.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZKSwizzle.h
3 | // ZKSwizzle
4 | //
5 | // Created by Alexander S Zielenski on 7/24/14.
6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 |
13 | // This is a class for streamlining swizzling. Simply create a new class of any name you want and
14 | // Example:
15 | /*
16 | @interface ZKHookClass : NSObject
17 | - (NSString *)description; // hooks -description on NSObject
18 | - (void)addedMethod; // all subclasses of NSObject now respond to -addedMethod
19 | @end
20 |
21 | @implementation ZKHookClass
22 | ...
23 | @end
24 |
25 | [ZKSwizzle swizzleClass:ZKClass(ZKHookClass) forClass:ZKClass(destination)];
26 | */
27 |
28 | #ifndef ZKSWIZZLE_DEFS
29 | #define ZKSWIZZLE_DEFS
30 |
31 | // CRAZY MACROS FOR DYNAMIC PROTOTYPE CREATION
32 | #define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5 ,4 ,3 ,2, 1, 0)
33 | #define VA_NUM_ARGS_IMPL(_0, _1,_2,_3,_4,_5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 ,N,...) N
34 |
35 | #define WRAP0()
36 | #define WRAP1(VARIABLE) , typeof ( VARIABLE )
37 | #define WRAP2(VARIABLE, ...) WRAP1(VARIABLE) WRAP1(__VA_ARGS__)
38 | #define WRAP3(VARIABLE, ...) WRAP1(VARIABLE) WRAP2(__VA_ARGS__)
39 | #define WRAP4(VARIABLE, ...) WRAP1(VARIABLE) WRAP3(__VA_ARGS__)
40 | #define WRAP5(VARIABLE, ...) WRAP1(VARIABLE) WRAP4(__VA_ARGS__)
41 | #define WRAP6(VARIABLE, ...) WRAP1(VARIABLE) WRAP5(__VA_ARGS__)
42 | #define WRAP7(VARIABLE, ...) WRAP1(VARIABLE) WRAP6(__VA_ARGS__)
43 | #define WRAP8(VARIABLE, ...) WRAP1(VARIABLE) WRAP7(__VA_ARGS__)
44 | #define WRAP9(VARIABLE, ...) WRAP1(VARIABLE) WRAP8(__VA_ARGS__)
45 | #define WRAP10(VARIABLE, ...) WRAP1(VARIABLE) WRAP9(__VA_ARGS__)
46 | #define WRAP11(VARIABLE, ...) WRAP1(VARIABLE) WRAP10(__VA_ARGS__)
47 | #define WRAP12(VARIABLE, ...) WRAP1(VARIABLE) WRAP11(__VA_ARGS__)
48 | #define WRAP13(VARIABLE, ...) WRAP1(VARIABLE) WRAP12(__VA_ARGS__)
49 | #define WRAP14(VARIABLE, ...) WRAP1(VARIABLE) WRAP13(__VA_ARGS__)
50 | #define WRAP15(VARIABLE, ...) WRAP1(VARIABLE) WRAP14(__VA_ARGS__)
51 | #define WRAP16(VARIABLE, ...) WRAP1(VARIABLE) WRAP15(__VA_ARGS__)
52 | #define WRAP17(VARIABLE, ...) WRAP1(VARIABLE) WRAP16(__VA_ARGS__)
53 | #define WRAP18(VARIABLE, ...) WRAP1(VARIABLE) WRAP17(__VA_ARGS__)
54 | #define WRAP19(VARIABLE, ...) WRAP1(VARIABLE) WRAP18(__VA_ARGS__)
55 | #define WRAP20(VARIABLE, ...) WRAP1(VARIABLE) WRAP19(__VA_ARGS__)
56 |
57 | #define CAT(A, B) A ## B
58 | #define INVOKE(MACRO, NUMBER, ...) CAT(MACRO, NUMBER)(__VA_ARGS__)
59 | #define WRAP_LIST(...) INVOKE(WRAP, VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
60 |
61 | // Gets the a class with the name CLASS
62 | #define ZKClass(CLASS) objc_getClass(#CLASS)
63 |
64 | // returns the value of an instance variable.
65 | #if !__has_feature(objc_arc)
66 | #define ZKHookIvar(OBJECT, TYPE, NAME) (*(TYPE *)ZKIvarPointer(OBJECT, NAME))
67 | #else
68 | #define ZKHookIvar(OBJECT, TYPE, NAME) \
69 | _Pragma("clang diagnostic push") \
70 | _Pragma("clang diagnostic ignored \"-Wignored-attributes\"") \
71 | (*(__unsafe_unretained TYPE *)ZKIvarPointer(OBJECT, NAME)) \
72 | _Pragma("clang diagnostic pop")
73 | #endif
74 | // returns the original implementation of the swizzled function or null or not found
75 | #define ZKOrig(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKOriginalImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__)
76 |
77 | // returns the original implementation of the superclass of the object swizzled
78 | #define ZKSuper(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKSuperImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__)
79 |
80 | #define _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, GROUP, IMMEDIATELY) \
81 | @interface _$ ## CLASS_NAME : SUPERCLASS @end \
82 | @implementation _$ ## CLASS_NAME \
83 | + (void)initialize {} \
84 | @end \
85 | @interface CLASS_NAME : _$ ## CLASS_NAME @end \
86 | @implementation CLASS_NAME (ZKSWIZZLE) \
87 | + (void)load { \
88 | _$ZKRegisterInterface(self, #GROUP);\
89 | if (IMMEDIATELY) { \
90 | [self _ZK_unconditionallySwizzle]; \
91 | } \
92 | } \
93 | + (void)_ZK_unconditionallySwizzle { \
94 | ZKSwizzle(CLASS_NAME, TARGET_CLASS); \
95 | } \
96 | @end
97 |
98 | // Bootstraps your swizzling class so that it requires no setup
99 | // outside of this macro call
100 | // If you override +load you must call ZKSwizzle(CLASS_NAME, TARGET_CLASS)
101 | // yourself, otherwise the swizzling would not take place
102 | #define ZKSwizzleInterface(CLASS_NAME, TARGET_CLASS, SUPERCLASS) \
103 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, ZK_UNGROUPED, YES)
104 |
105 | // Same as ZKSwizzleInterface, except
106 | #define ZKSwizzleInterfaceGroup(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP) \
107 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP, NO)
108 |
109 | __BEGIN_DECLS
110 |
111 | // Make sure to cast this before you use it
112 | typedef id (*ZKIMP)(id, SEL, ...);
113 |
114 | // returns a pointer to the instance variable "name" on the object
115 | void *ZKIvarPointer(id self, const char *name);
116 | // returns the original implementation of a method with selector "sel" of an object hooked by the methods below
117 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info);
118 | // returns the implementation of a method with selector "sel" of the superclass of object
119 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info);
120 |
121 | // hooks all the implemented methods of source with destination
122 | // adds any methods that arent implemented on destination to destination that are implemented in source
123 | #define ZKSwizzle(src, dst) _ZKSwizzle(ZKClass(src), ZKClass(dst))
124 | BOOL _ZKSwizzle(Class src, Class dest);
125 |
126 | #define ZKSwizzleGroup(NAME) _ZKSwizzleGroup(#NAME)
127 | void _$ZKRegisterInterface(Class cls, const char *groupName);
128 | BOOL _ZKSwizzleGroup(const char *groupName);
129 |
130 | // Calls above method with the superclass of source for desination
131 | #define ZKSwizzleClass(src) _ZKSwizzleClass(ZKClass(src))
132 | BOOL _ZKSwizzleClass(Class cls);
133 |
134 | __END_DECLS
135 | #endif
136 |
137 |
--------------------------------------------------------------------------------
/fullscreenER.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | FBB53C9C1D0BC9B400DD182D /* ZKSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB53C9B1D0BC9B400DD182D /* ZKSwizzle.m */; };
11 | FBB53C9F1D0BC9EF00DD182D /* fullscreenER.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB53C9E1D0BC9EF00DD182D /* fullscreenER.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXFileReference section */
15 | FBB53C941D0BC99600DD182D /* fullscreenER.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = fullscreenER.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
16 | FBB53C9A1D0BC9B400DD182D /* ZKSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZKSwizzle.h; path = ZKSwizzle/ZKSwizzle.h; sourceTree = ""; };
17 | FBB53C9B1D0BC9B400DD182D /* ZKSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ZKSwizzle.m; path = ZKSwizzle/ZKSwizzle.m; sourceTree = ""; };
18 | FBB53C9D1D0BC9EF00DD182D /* fullscreenER.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fullscreenER.h; sourceTree = ""; };
19 | FBB53C9E1D0BC9EF00DD182D /* fullscreenER.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fullscreenER.m; sourceTree = ""; };
20 | FBB53CA11D0BCB8B00DD182D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
21 | /* End PBXFileReference section */
22 |
23 | /* Begin PBXFrameworksBuildPhase section */
24 | FBB53C911D0BC99600DD182D /* Frameworks */ = {
25 | isa = PBXFrameworksBuildPhase;
26 | buildActionMask = 2147483647;
27 | files = (
28 | );
29 | runOnlyForDeploymentPostprocessing = 0;
30 | };
31 | /* End PBXFrameworksBuildPhase section */
32 |
33 | /* Begin PBXGroup section */
34 | FBB53C951D0BC99600DD182D /* fullscreenER */ = {
35 | isa = PBXGroup;
36 | children = (
37 | FBB53CA01D0BCA5E00DD182D /* ZKSwizzle */,
38 | FBB53C9D1D0BC9EF00DD182D /* fullscreenER.h */,
39 | FBB53C9E1D0BC9EF00DD182D /* fullscreenER.m */,
40 | FBB53CA11D0BCB8B00DD182D /* Info.plist */,
41 | );
42 | path = fullscreenER;
43 | sourceTree = "";
44 | };
45 | FBB53CA01D0BCA5E00DD182D /* ZKSwizzle */ = {
46 | isa = PBXGroup;
47 | children = (
48 | FBB53C9A1D0BC9B400DD182D /* ZKSwizzle.h */,
49 | FBB53C9B1D0BC9B400DD182D /* ZKSwizzle.m */,
50 | );
51 | name = ZKSwizzle;
52 | sourceTree = "";
53 | };
54 | FBFEDC171BA261DE009B4689 = {
55 | isa = PBXGroup;
56 | children = (
57 | FBB53C951D0BC99600DD182D /* fullscreenER */,
58 | FBFEDC211BA261DE009B4689 /* Products */,
59 | );
60 | sourceTree = "";
61 | };
62 | FBFEDC211BA261DE009B4689 /* Products */ = {
63 | isa = PBXGroup;
64 | children = (
65 | FBB53C941D0BC99600DD182D /* fullscreenER.bundle */,
66 | );
67 | name = Products;
68 | sourceTree = "";
69 | };
70 | /* End PBXGroup section */
71 |
72 | /* Begin PBXNativeTarget section */
73 | FBB53C931D0BC99600DD182D /* fullscreenER */ = {
74 | isa = PBXNativeTarget;
75 | buildConfigurationList = FBB53C971D0BC99600DD182D /* Build configuration list for PBXNativeTarget "fullscreenER" */;
76 | buildPhases = (
77 | FBB53C901D0BC99600DD182D /* Sources */,
78 | FBB53C911D0BC99600DD182D /* Frameworks */,
79 | FBB53C921D0BC99600DD182D /* Resources */,
80 | );
81 | buildRules = (
82 | );
83 | dependencies = (
84 | );
85 | name = fullscreenER;
86 | productName = fullscreenER;
87 | productReference = FBB53C941D0BC99600DD182D /* fullscreenER.bundle */;
88 | productType = "com.apple.product-type.bundle";
89 | };
90 | /* End PBXNativeTarget section */
91 |
92 | /* Begin PBXProject section */
93 | FBFEDC181BA261DE009B4689 /* Project object */ = {
94 | isa = PBXProject;
95 | attributes = {
96 | LastUpgradeCheck = 0900;
97 | ORGANIZATIONNAME = "Wolfgang Baird";
98 | TargetAttributes = {
99 | FBB53C931D0BC99600DD182D = {
100 | CreatedOnToolsVersion = 7.3;
101 | };
102 | };
103 | };
104 | buildConfigurationList = FBFEDC1B1BA261DE009B4689 /* Build configuration list for PBXProject "fullscreenER" */;
105 | compatibilityVersion = "Xcode 3.2";
106 | developmentRegion = English;
107 | hasScannedForEncodings = 0;
108 | knownRegions = (
109 | en,
110 | );
111 | mainGroup = FBFEDC171BA261DE009B4689;
112 | productRefGroup = FBFEDC211BA261DE009B4689 /* Products */;
113 | projectDirPath = "";
114 | projectRoot = "";
115 | targets = (
116 | FBB53C931D0BC99600DD182D /* fullscreenER */,
117 | );
118 | };
119 | /* End PBXProject section */
120 |
121 | /* Begin PBXResourcesBuildPhase section */
122 | FBB53C921D0BC99600DD182D /* Resources */ = {
123 | isa = PBXResourcesBuildPhase;
124 | buildActionMask = 2147483647;
125 | files = (
126 | );
127 | runOnlyForDeploymentPostprocessing = 0;
128 | };
129 | /* End PBXResourcesBuildPhase section */
130 |
131 | /* Begin PBXSourcesBuildPhase section */
132 | FBB53C901D0BC99600DD182D /* Sources */ = {
133 | isa = PBXSourcesBuildPhase;
134 | buildActionMask = 2147483647;
135 | files = (
136 | FBB53C9C1D0BC9B400DD182D /* ZKSwizzle.m in Sources */,
137 | FBB53C9F1D0BC9EF00DD182D /* fullscreenER.m in Sources */,
138 | );
139 | runOnlyForDeploymentPostprocessing = 0;
140 | };
141 | /* End PBXSourcesBuildPhase section */
142 |
143 | /* Begin XCBuildConfiguration section */
144 | FBB53C981D0BC99600DD182D /* Debug */ = {
145 | isa = XCBuildConfiguration;
146 | buildSettings = {
147 | CLANG_ANALYZER_NONNULL = YES;
148 | CODE_SIGN_IDENTITY = "-";
149 | COMBINE_HIDPI_IMAGES = YES;
150 | INFOPLIST_FILE = fullscreenER/Info.plist;
151 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
152 | MACOSX_DEPLOYMENT_TARGET = 10.9;
153 | PRODUCT_BUNDLE_IDENTIFIER = org.w0lf.fullscreenER;
154 | PRODUCT_NAME = "$(TARGET_NAME)";
155 | SKIP_INSTALL = YES;
156 | WRAPPER_EXTENSION = bundle;
157 | };
158 | name = Debug;
159 | };
160 | FBB53C991D0BC99600DD182D /* Release */ = {
161 | isa = XCBuildConfiguration;
162 | buildSettings = {
163 | CLANG_ANALYZER_NONNULL = YES;
164 | CODE_SIGN_IDENTITY = "-";
165 | COMBINE_HIDPI_IMAGES = YES;
166 | INFOPLIST_FILE = fullscreenER/Info.plist;
167 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
168 | MACOSX_DEPLOYMENT_TARGET = 10.9;
169 | PRODUCT_BUNDLE_IDENTIFIER = org.w0lf.fullscreenER;
170 | PRODUCT_NAME = "$(TARGET_NAME)";
171 | SKIP_INSTALL = YES;
172 | WRAPPER_EXTENSION = bundle;
173 | };
174 | name = Release;
175 | };
176 | FBFEDC241BA261DF009B4689 /* Debug */ = {
177 | isa = XCBuildConfiguration;
178 | buildSettings = {
179 | ALWAYS_SEARCH_USER_PATHS = NO;
180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
181 | CLANG_CXX_LIBRARY = "libc++";
182 | CLANG_ENABLE_MODULES = YES;
183 | CLANG_ENABLE_OBJC_ARC = YES;
184 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
185 | CLANG_WARN_BOOL_CONVERSION = YES;
186 | CLANG_WARN_COMMA = YES;
187 | CLANG_WARN_CONSTANT_CONVERSION = YES;
188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
189 | CLANG_WARN_EMPTY_BODY = YES;
190 | CLANG_WARN_ENUM_CONVERSION = YES;
191 | CLANG_WARN_INFINITE_RECURSION = YES;
192 | CLANG_WARN_INT_CONVERSION = YES;
193 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
194 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
196 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
197 | CLANG_WARN_STRICT_PROTOTYPES = YES;
198 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_DYNAMIC_NO_PIC = NO;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_OPTIMIZATION_LEVEL = 0;
209 | GCC_PREPROCESSOR_DEFINITIONS = (
210 | "DEBUG=1",
211 | "$(inherited)",
212 | );
213 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
214 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
215 | GCC_WARN_UNDECLARED_SELECTOR = YES;
216 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
217 | GCC_WARN_UNUSED_FUNCTION = YES;
218 | GCC_WARN_UNUSED_VARIABLE = YES;
219 | MACOSX_DEPLOYMENT_TARGET = 10.9;
220 | MTL_ENABLE_DEBUG_INFO = YES;
221 | ONLY_ACTIVE_ARCH = YES;
222 | SDKROOT = macosx;
223 | };
224 | name = Debug;
225 | };
226 | FBFEDC251BA261DF009B4689 /* Release */ = {
227 | isa = XCBuildConfiguration;
228 | buildSettings = {
229 | ALWAYS_SEARCH_USER_PATHS = NO;
230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
231 | CLANG_CXX_LIBRARY = "libc++";
232 | CLANG_ENABLE_MODULES = YES;
233 | CLANG_ENABLE_OBJC_ARC = YES;
234 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
235 | CLANG_WARN_BOOL_CONVERSION = YES;
236 | CLANG_WARN_COMMA = YES;
237 | CLANG_WARN_CONSTANT_CONVERSION = YES;
238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
239 | CLANG_WARN_EMPTY_BODY = YES;
240 | CLANG_WARN_ENUM_CONVERSION = YES;
241 | CLANG_WARN_INFINITE_RECURSION = YES;
242 | CLANG_WARN_INT_CONVERSION = YES;
243 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
244 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
246 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
247 | CLANG_WARN_STRICT_PROTOTYPES = YES;
248 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
249 | CLANG_WARN_UNREACHABLE_CODE = YES;
250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
251 | COPY_PHASE_STRIP = NO;
252 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
253 | ENABLE_NS_ASSERTIONS = NO;
254 | ENABLE_STRICT_OBJC_MSGSEND = YES;
255 | GCC_C_LANGUAGE_STANDARD = gnu99;
256 | GCC_NO_COMMON_BLOCKS = YES;
257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
259 | GCC_WARN_UNDECLARED_SELECTOR = YES;
260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
261 | GCC_WARN_UNUSED_FUNCTION = YES;
262 | GCC_WARN_UNUSED_VARIABLE = YES;
263 | MACOSX_DEPLOYMENT_TARGET = 10.9;
264 | MTL_ENABLE_DEBUG_INFO = NO;
265 | SDKROOT = macosx;
266 | };
267 | name = Release;
268 | };
269 | /* End XCBuildConfiguration section */
270 |
271 | /* Begin XCConfigurationList section */
272 | FBB53C971D0BC99600DD182D /* Build configuration list for PBXNativeTarget "fullscreenER" */ = {
273 | isa = XCConfigurationList;
274 | buildConfigurations = (
275 | FBB53C981D0BC99600DD182D /* Debug */,
276 | FBB53C991D0BC99600DD182D /* Release */,
277 | );
278 | defaultConfigurationIsVisible = 0;
279 | defaultConfigurationName = Release;
280 | };
281 | FBFEDC1B1BA261DE009B4689 /* Build configuration list for PBXProject "fullscreenER" */ = {
282 | isa = XCConfigurationList;
283 | buildConfigurations = (
284 | FBFEDC241BA261DF009B4689 /* Debug */,
285 | FBFEDC251BA261DF009B4689 /* Release */,
286 | );
287 | defaultConfigurationIsVisible = 0;
288 | defaultConfigurationName = Release;
289 | };
290 | /* End XCConfigurationList section */
291 | };
292 | rootObject = FBFEDC181BA261DE009B4689 /* Project object */;
293 | }
294 |
--------------------------------------------------------------------------------
/fullscreenER/ZKSwizzle/ZKSwizzle.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZKSwizzle.m
3 | // ZKSwizzle
4 | //
5 | // Created by Alexander S Zielenski on 7/24/14.
6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved.
7 | //
8 |
9 | #import "ZKSwizzle.h"
10 | static NSMutableDictionary *classTable;
11 |
12 | @interface NSObject (ZKSwizzle)
13 | + (void)_ZK_unconditionallySwizzle;
14 | @end
15 |
16 | void *ZKIvarPointer(id self, const char *name) {
17 | Ivar ivar = class_getInstanceVariable(object_getClass(self), name);
18 | return ivar == NULL ? NULL : (__bridge void *)self + ivar_getOffset(ivar);
19 | }
20 |
21 | static SEL destinationSelectorForSelector(SEL cmd, Class dst) {
22 | return NSSelectorFromString([@"_ZK_old_" stringByAppendingFormat:@"%s_%@", class_getName(dst), NSStringFromSelector(cmd)]);
23 | }
24 |
25 | static Class classFromInfo(const char *info) {
26 | NSUInteger bracket_index = -1;
27 | for (NSUInteger i = 0; i < strlen(info); i++) {
28 | if (info[i] == '[') {
29 | bracket_index = i;
30 | }
31 | }
32 | bracket_index++;
33 |
34 | if (bracket_index == -1) {
35 | [NSException raise:@"Failed to parse info" format:@"Couldn't find swizzle class for info: %s", info];
36 | return NULL;
37 | }
38 |
39 | char after_bracket[255];
40 | memcpy(after_bracket, &info[bracket_index], strlen(info) - bracket_index - 1);
41 |
42 | for (NSUInteger i = 0; i < strlen(info); i++) {
43 | if (after_bracket[i] == ' ') {
44 | after_bracket[i] = '\0';
45 | }
46 | }
47 |
48 | return objc_getClass(after_bracket);
49 | }
50 |
51 | // takes __PRETTY_FUNCTION__ for info which gives the name of the swizzle source class
52 | /*
53 |
54 | We add the original implementation onto the swizzle class
55 | On ZKOrig, we use __PRETTY_FUNCTION__ to get the name of the swizzle class
56 | Then we get the implementation of that selector on the swizzle class
57 | Then we call it directly, passing in the correct selector and self
58 |
59 | */
60 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info) {
61 | if (sel == NULL || self == NULL || info == NULL) {
62 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@, or info: %s is NULL", self, NSStringFromSelector(sel), info];
63 | return NULL;
64 | }
65 |
66 | Class cls = classFromInfo(info);
67 | Class dest = object_getClass(self);
68 |
69 | if (cls == NULL || dest == NULL) {
70 | [NSException raise:@"Failed obtain class pair" format:@"src: %@ | dst: %@ | sel: %@", NSStringFromClass(cls), NSStringFromClass(dest), NSStringFromSelector(sel)];
71 | return NULL;
72 | }
73 |
74 | SEL destSel = destinationSelectorForSelector(sel, cls);
75 |
76 | Method method = class_getInstanceMethod(dest, destSel);
77 |
78 | if (method == NULL) {
79 | [NSException raise:@"Failed to retrieve method" format:@"Got null for the source class %@ with selector %@ (%@)", NSStringFromClass(cls), NSStringFromSelector(sel), NSStringFromSelector(destSel)];
80 | return NULL;
81 | }
82 |
83 | ZKIMP implementation = (ZKIMP)method_getImplementation(method);
84 | if (implementation == NULL) {
85 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)];
86 | }
87 |
88 | return implementation;
89 | }
90 |
91 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info) {
92 | if (sel == NULL || object == NULL) {
93 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@ is NULL", object, NSStringFromSelector(sel)];
94 | return NULL;
95 | }
96 |
97 | Class cls = object_getClass(object);
98 | if (cls == NULL) {
99 | [NSException raise:@"Invalid Argument" format:@"Could not obtain class for the passed object"];
100 | return NULL;
101 | }
102 |
103 | // Two scenarios:
104 | // 1.) The superclass was not swizzled, no problem
105 | // 2.) The superclass was swizzled, problem
106 |
107 | // We want to return the swizzled class's superclass implementation
108 | // If this is a subclass of such a class, we want two behaviors:
109 | // a.) If this imp was also swizzled, no problem, return the superclass's swizzled imp
110 | // b.) This imp was not swizzled, return the class that was originally swizzled's superclass's imp
111 | Class sourceClass = classFromInfo(info);
112 | if (sourceClass != NULL) {
113 | BOOL isClassMethod = class_isMetaClass(cls);
114 | // This was called from a swizzled method, get the class it was swizzled with
115 | NSString *className = classTable[NSStringFromClass(sourceClass)];
116 | if (className != NULL) {
117 | cls = NSClassFromString(className);
118 | // make sure we get a class method if we asked for one
119 | if (isClassMethod) {
120 | cls = object_getClass(cls);
121 | }
122 | }
123 | }
124 |
125 | cls = class_getSuperclass(cls);
126 |
127 | // This is a root class, it has no super class
128 | if (cls == NULL) {
129 | [NSException raise:@"Invalid Argument" format:@"Could not obtain superclass for the passed object"];
130 | return NULL;
131 | }
132 |
133 | Method method = class_getInstanceMethod(cls, sel);
134 | if (method == NULL) {
135 | [NSException raise:@"Failed to retrieve method" format:@"We could not find the super implementation for the class %@ and selector %@, are you sure it exists?", NSStringFromClass(cls), NSStringFromSelector(sel)];
136 | return NULL;
137 | }
138 |
139 | ZKIMP implementation = (ZKIMP)method_getImplementation(method);
140 | if (implementation == NULL) {
141 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)];
142 | }
143 |
144 | return implementation;
145 | }
146 |
147 | static BOOL enumerateMethods(Class, Class);
148 | BOOL _ZKSwizzle(Class src, Class dest) {
149 | if (dest == NULL)
150 | return NO;
151 |
152 | NSString *destName = NSStringFromClass(dest);
153 | if (!destName) {
154 | return NO;
155 | }
156 |
157 | if (!classTable) {
158 | classTable = [[NSMutableDictionary alloc] init];
159 | }
160 |
161 | if ([classTable objectForKey:NSStringFromClass(src)]) {
162 | [NSException raise:@"Invalid Argument"
163 | format:@"This source class (%@) was already swizzled with another, (%@)", NSStringFromClass(src), classTable[NSStringFromClass(src)]];
164 | return NO;
165 | }
166 |
167 | BOOL success = enumerateMethods(dest, src);
168 | // The above method only gets instance methods. Do the same method for the metaclass of the class
169 | success &= enumerateMethods(object_getClass(dest), object_getClass(src));
170 |
171 | [classTable setObject:destName forKey:NSStringFromClass(src)];
172 | return success;
173 | }
174 |
175 | BOOL _ZKSwizzleClass(Class cls) {
176 | return _ZKSwizzle(cls, [cls superclass]);
177 | }
178 |
179 | static BOOL enumerateMethods(Class destination, Class source) {
180 | #if OBJC_API_VERSION < 2
181 | [NSException raise:@"Unsupported feature" format:@"ZKSwizzle is only available in objc 2.0"];
182 | return NO;
183 |
184 | #else
185 |
186 | unsigned int methodCount;
187 | Method *methodList = class_copyMethodList(source, &methodCount);
188 | BOOL success = YES;
189 | for (int i = 0; i < methodCount; i++) {
190 | Method method = methodList[i];
191 | SEL selector = method_getName(method);
192 | NSString *methodName = NSStringFromSelector(selector);
193 |
194 | // Don't do anything with the unconditional swizzle
195 | if (sel_isEqual(selector, @selector(_ZK_unconditionallySwizzle))) {
196 | continue;
197 | }
198 |
199 | // We only swizzle methods that are implemented
200 | if (class_respondsToSelector(destination, selector)) {
201 | Method originalMethod = class_getInstanceMethod(destination, selector);
202 |
203 | const char *originalType = method_getTypeEncoding(originalMethod);
204 | const char *newType = method_getTypeEncoding(method);
205 | if (strcmp(originalType, newType) != 0) {
206 | NSLog(@"ZKSwizzle: incompatible type encoding for %@. (expected %s, got %s)", methodName, originalType, newType);
207 | // Incompatible type encoding
208 | success = NO;
209 | continue;
210 | }
211 |
212 | // We are re-adding the destination selector because it could be on a superclass and not on the class itself. This method could fail
213 | class_addMethod(destination, selector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
214 |
215 | SEL destSel = destinationSelectorForSelector(selector, source);
216 | if (!class_addMethod(destination, destSel, method_getImplementation(method), method_getTypeEncoding(originalMethod))) {
217 | NSLog(@"ZKSwizzle: failed to add method %@ onto class %@ with selector %@", NSStringFromSelector(selector), NSStringFromClass(source), NSStringFromSelector(destSel));
218 | success = NO;
219 | continue;
220 | }
221 |
222 | method_exchangeImplementations(class_getInstanceMethod(destination, selector), class_getInstanceMethod(destination, destSel));
223 | } else {
224 | // Add any extra methods to the class but don't swizzle them
225 | success &= class_addMethod(destination, selector, method_getImplementation(method), method_getTypeEncoding(method));
226 | }
227 | }
228 |
229 | unsigned int propertyCount;
230 | objc_property_t *propertyList = class_copyPropertyList(source, &propertyCount);
231 | for (int i = 0; i < propertyCount; i++) {
232 | objc_property_t property = propertyList[i];
233 | const char *name = property_getName(property);
234 | unsigned int attributeCount;
235 | objc_property_attribute_t *attributes = property_copyAttributeList(property, &attributeCount);
236 |
237 | if (class_getProperty(destination, name) == NULL) {
238 | class_addProperty(destination, name, attributes, attributeCount);
239 | } else {
240 | class_replaceProperty(destination, name, attributes, attributeCount);
241 | }
242 |
243 | free(attributes);
244 | }
245 |
246 | free(propertyList);
247 | free(methodList);
248 | return success;
249 | #endif
250 | }
251 |
252 | // Options were to use a group class and traverse its subclasses
253 | // or to create a groups dictionary
254 | // This works because +load on NSObject is called before attribute((constructor))
255 | static NSMutableDictionary *groups = nil;
256 | void _$ZKRegisterInterface(Class cls, const char *groupName) {
257 | if (!groups)
258 | groups = [NSMutableDictionary dictionary];
259 |
260 | NSString *groupString = @(groupName);
261 | NSMutableArray *groupList = groups[groupString];
262 | if (!groupList) {
263 | groupList = [NSMutableArray array];
264 | groups[groupString] = groupList;
265 | }
266 |
267 | [groupList addObject:NSStringFromClass(cls)];
268 | }
269 |
270 | BOOL _ZKSwizzleGroup(const char *groupName) {
271 | NSArray *groupList = groups[@(groupName)];
272 | if (!groupList) {
273 | [NSException raise:@"Invalid Argument" format:@"ZKSwizzle: There is no group by the name of %s", groupName];
274 | return NO;
275 | }
276 |
277 | BOOL success = YES;
278 | for (NSString *className in groupList) {
279 | Class cls = NSClassFromString(className);
280 | if (cls == NULL)
281 | continue;
282 |
283 | if (class_respondsToSelector(object_getClass(cls), @selector(_ZK_unconditionallySwizzle))) {
284 | [cls _ZK_unconditionallySwizzle];
285 | } else {
286 | success = NO;
287 | }
288 | }
289 |
290 | return success;
291 | }
292 |
--------------------------------------------------------------------------------