iosobjective-cmethod-swizzling

UIViewController method swizzling not working


I am using a category on UIViewController to swizzle the viewWillAppear: method across multiple ViewControllers.

@implementation UIViewController (Tracking)

+(void)load
{
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xx_viewWillAppear:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

        if (didAddMethod)
        {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }else{
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }

    });
}

-(void)xx_viewWillAppear:(BOOL)animated
{
    [self xx_viewWillAppear:animated];

    NSLog(@"VC loaded");
}

@end

When I load the initial VC from Storyboard, the swizzled method is never called. It is called only if the viewWillAppear method in VC is commented out. However when I remove the category and move the code into the individual VC, both the methods are called.

#import "ViewController.h"
#import "UIViewController+Tracking.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

-(void)viewWillAppear:(BOOL)animated
{
    NSLog(@"Inside Controller1");
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Is it ok to implement the viewWillAppear in VC if it is going to be swizzled? If not, how do I handle this, if I am required to write some code in VC's viewWillAppear?


Solution

  • You need to call super in your viewWillAppear: from ViewController, otherwise the method from the base class (UIViewController) won't execute, and that's the one that you swizzled.

    The following version of viewWillAppear: should give you the expected results:

    -(void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        NSLog(@"Inside Controller1");
    }