Created
September 1, 2016 07:01
-
-
Save jcayzac/4553b454d9acbdcd39730eea177bff35 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "AppDelegate.h" | |
/* | |
* Note our application delegate doesn't implement -applicationDidEnterBackground:, but it doesn't matter. | |
*/ | |
@implementation AppDelegate | |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | |
NSLog(@"Original -application:didFinishLaunchingWithOptions: (self.class is %@)", self.class); | |
return YES; | |
} | |
- (void)applicationWillEnterForeground:(UIApplication *)application { | |
NSLog(@"Original -applicationWillEnterForeground: (self.class is %@)", self.class); | |
} | |
@end | |
@interface ViewController : UIViewController | |
@end | |
@implementation ViewController | |
@end | |
/* | |
* _ClassHooks is a base class for implementing hooks. | |
*/ | |
@import ObjectiveC.runtime; | |
@interface _ClassHooks : NSObject | |
+ (void)hookSelector:(SEL)hookSelector targetSelector:(SEL)targetSelector class:(Class)class; | |
@end | |
@implementation _ClassHooks | |
+ (void)hookSelector:(SEL)hookSelector targetSelector:(SEL)targetSelector class:(Class)class { | |
SEL childSelector = targetSelector; | |
Method hook = class_getInstanceMethod(self, hookSelector); | |
Method method = class_getInstanceMethod(class, targetSelector); | |
if (method) { | |
method_exchangeImplementations(hook, method); | |
childSelector = hookSelector; | |
} | |
if (!class_getInstanceMethod(class, childSelector)) { | |
class_addMethod(class, childSelector, method_getImplementation(hook), method_getTypeEncoding(hook)); | |
} | |
} | |
@end | |
/* | |
* Our hooks are implemented in _DummyHooks: | |
* - First, it hooks UIApplication.setDelegate(), so that in can install the other hooks | |
* after there is an application delegate set, but before -application:didFinishLaunchingWithOptions: is called. | |
* - Then, it hooks all the UIApplicationDelegate methods we're interested in. It does not matter whether | |
* the methods are actually implemented by the application delegate or not. | |
*/ | |
@interface _DummyHooks : _ClassHooks<UIApplicationDelegate> | |
@end | |
@implementation _DummyHooks | |
#pragma mark - UIViewController hooks | |
- (void)_hooked_viewWillAppear:(BOOL)animated { | |
NSLog(@"%@ will be called now on %@", NSStringFromSelector(_cmd), self); | |
if ([self respondsToSelector:@selector(_hooked_viewWillAppear:)]) { | |
[self _hooked_viewWillAppear:animated]; | |
} | |
NSLog(@"%@ was just called on %@", NSStringFromSelector(_cmd), self); | |
} | |
- (void)_hooked_viewWillDisappear:(BOOL)animated { | |
NSLog(@"%@ will be called now on %@", NSStringFromSelector(_cmd), self); | |
if ([self respondsToSelector:@selector(_hooked_viewWillDisappear:)]) { | |
[self _hooked_viewWillDisappear:animated]; | |
} | |
NSLog(@"%@ was just called on %@", NSStringFromSelector(_cmd), self); | |
} | |
#pragma mark - UIApplicationDelegate hooks | |
- (BOOL)_hooked_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | |
BOOL ret = YES; | |
NSLog(@"%@ will be called now! launchOptions is %@", NSStringFromSelector(_cmd), launchOptions); | |
if ([self respondsToSelector:@selector(_hooked_application:didFinishLaunchingWithOptions:)]) { | |
ret = [self _hooked_application:application didFinishLaunchingWithOptions:launchOptions]; | |
} else { | |
NSLog(@"%@ does not implement %@.", self.class, NSStringFromSelector(_cmd)); | |
} | |
NSLog(@"%@ was just called!", NSStringFromSelector(_cmd)); | |
return ret; | |
} | |
- (void)_hooked_applicationWillEnterForeground:(UIApplication *)application { | |
NSLog(@"%@ will be called now!", NSStringFromSelector(_cmd)); | |
if ([self respondsToSelector:@selector(_hooked_applicationWillEnterForeground:)]) { | |
[self _hooked_applicationWillEnterForeground:application]; | |
} else { | |
NSLog(@"%@ does not implement %@.", self.class, NSStringFromSelector(_cmd)); | |
} | |
NSLog(@"%@ was just called!", NSStringFromSelector(_cmd)); | |
} | |
- (void)_hooked_applicationDidEnterBackground:(UIApplication *)application { | |
NSLog(@"%@ will be called now!", NSStringFromSelector(_cmd)); | |
if ([self respondsToSelector:@selector(_hooked_applicationDidEnterBackground:)]) { | |
[self _hooked_applicationDidEnterBackground:application]; | |
} else { | |
NSLog(@"%@ does not implement %@.", self.class, NSStringFromSelector(_cmd)); | |
} | |
NSLog(@"%@ was just called!", NSStringFromSelector(_cmd)); | |
} | |
#pragma mark - UIApplication hooks | |
- (void)_hooked_setApplicationDelegate:(id<UIApplicationDelegate>)delegate { | |
NSLog(@"%@ will be called now!", NSStringFromSelector(_cmd)); | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
// Install the delegate's hooks | |
Class cls = delegate.class; | |
NSLog(@"Concrete delegate class: %@", cls); | |
[_DummyHooks hookSelector:@selector(_hooked_application:didFinishLaunchingWithOptions:) | |
targetSelector:@selector(application:didFinishLaunchingWithOptions:) | |
class:cls]; | |
[_DummyHooks hookSelector:@selector(_hooked_applicationWillEnterForeground:) | |
targetSelector:@selector(applicationWillEnterForeground:) | |
class:cls]; | |
[_DummyHooks hookSelector:@selector(_hooked_applicationDidEnterBackground:) | |
targetSelector:@selector(applicationDidEnterBackground:) | |
class:cls]; | |
}); | |
[self _hooked_setApplicationDelegate:delegate]; | |
NSLog(@"%@ was just called!", NSStringFromSelector(_cmd)); | |
} | |
+ (void)load { | |
/* | |
* This will install all the hooks | |
*/ | |
[_DummyHooks hookSelector:@selector(_hooked_setApplicationDelegate:) | |
targetSelector:@selector(setDelegate:) | |
class:UIApplication.class]; | |
[_DummyHooks hookSelector:@selector(_hooked_viewWillAppear:) | |
targetSelector:@selector(viewWillAppear:) | |
class:UIViewController.class]; | |
[_DummyHooks hookSelector:@selector(_hooked_viewWillDisappear:) | |
targetSelector:@selector(viewWillDisappear:) | |
class:UIViewController.class]; | |
} | |
@end | |
/* | |
* Example log: | |
* | |
* APP LAUNCH: | |
* setDelegate: will be called now! | |
* Concrete delegate class: AppDelegate | |
* setDelegate: was just called! | |
* application:didFinishLaunchingWithOptions: will be called now! launchOptions is (null) | |
* Original -application:didFinishLaunchingWithOptions: (self.class is AppDelegate) | |
* application:didFinishLaunchingWithOptions: was just called! | |
* viewWillAppear: will be called now on <ViewController: 0x7fb1d1c420c0> | |
* viewWillAppear: was just called on <ViewController: 0x7fb1d1c420c0> | |
* viewWillAppear: will be called now on <UIInputWindowController: 0x7fb1d202da00> | |
* viewWillAppear: was just called on <UIInputWindowController: 0x7fb1d202da00> | |
* | |
* HOME BUTTON: | |
* applicationDidEnterBackground: will be called now! | |
* AppDelegate does not implement applicationDidEnterBackground:. | |
* applicationDidEnterBackground: was just called! | |
* | |
* TAP APP AGAIN: | |
* applicationWillEnterForeground: will be called now! | |
* Original -applicationWillEnterForeground: (self.class is AppDelegate) | |
* applicationWillEnterForeground: was just called! | |
* | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment