Skip to content

Instantly share code, notes, and snippets.

@jcayzac
Created September 1, 2016 07:01
Show Gist options
  • Save jcayzac/4553b454d9acbdcd39730eea177bff35 to your computer and use it in GitHub Desktop.
Save jcayzac/4553b454d9acbdcd39730eea177bff35 to your computer and use it in GitHub Desktop.
#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