├── 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 | ![preview](preview.gif) 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 | --------------------------------------------------------------------------------