Wednesday, February 15, 2017

ios using swizzle to intercept and call original method for debug purpose

Swizzle can be used to intercept ios method invocation for debug purpose, so you can check when a certain method is called with what parameter.

The swizzled method can log the necessary information, and then call the original method so that the original behavior will not be changed.

The following example intercepts UIWebView's setFrame method to catch and find out why the UIWebView's frame is resized in certain cases.

The first step is declaring a category for adding a new method for the UIWebView, the new method will replace the original setFrame method. The reason to use category is both the new method and original method should be defined on the same class, otherwise, the new method cannot call back into the original method if they are defined on different class

The second step is applying the swizzle to replace the original method with the new method, there is some template code to do so.

The last step is implementing the category's new method implementation, it should first log the necessary information, then call the new method. Note, in order to call into the original method implementation, the code needs to call the new method's signature, which will internally call the original method's implementation.

The below code sample shows the example of this approach:

step 1:
@interface UIWebView (SetFrame)
    -(void)newSetFrame:(CGRect) rect;
@end


step 2:
//run this code during app initialization step
    SEL originalSelector = @selector(setFrame:);
    SEL swizzledSelector = @selector(newSetFrame:);
    Method originalMethod = class_getInstanceMethod([UIWebView class], originalSelector);
    Method swizzledMethod = class_getInstanceMethod([UIWebView class], swizzledSelector);
    
    BOOL didAddMethod = class_addMethod([UIWebView class],
                                        originalSelector,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod([UIWebView class],
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

step 3:
@implementation UIWebView (SetFrame)
-(void)newSetFrame:(CGRect) rect{
    NSLog(@"uiWebview override setFrame: %@", NSStringFromCGRect(rect));
    [self newSetFrame:rect]; 
 }

@end

No comments:

Post a Comment