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

Friday, February 10, 2017

How to replace android library project with source code for debugging in android studio

In order to debug the source code of android library project in android studio, the following steps can be used.
1. in android studio's application project, open build.gradle
2. if the library project gradle file is included in it, which means the library project is built into aar file, then comment it out as below
   apply from: "kapsel-plugin-fioriclient/local-smp_fioriclient.gradle"
   //apply from: "kapsel-plugin-attachmentviewer/local-smp_attachmentviewer.gradle"
   apply from: "kapsel-plugin-voicerecording/local-smp_voicerecording.gradle"

   if the library project's gradle file is not specified in build.gradle, then search the project's lib folder to find the jar file, and then rename the jar file to a different name, so that it cannot be loaded

3. add the source file into the main application project. open build.gradle, and search java.srcDirs, include the parent folder of library's "com" source folder in the  folder array as below
java.srcDirs = ['src','/Users/i826633/Documents/source/kapsel/com.sap.mobile.platform.client.kapsel.plugins.android-3.14/attachmentviewer/src/main/java','/Users/i826633/Documents/source/kapsel/com.sap.mobile.platform.client.kapsel.plugins.android-3.14/online/src/main/java','/Users/i826633/Documents/source/kapsel/com.sap.mobile.platform.client.kapsel.plugins.android-3.14/authproxy/src/main/java']

4. sync the gradle and build the project, some error of R. missing resource may happen as only the java source code is added in the main project, and the resource is missing. Just comment out the lines causing the error, or replace the resource id with the actual resource

5. build and run the project

Thursday, February 2, 2017

About URL encoding

When browser sends request to server using http requests, the url cannot have special characters, like space or colon. If the url includes the special characters, they need to be encoded.

Based on http://www.ietf.org/rfc/rfc2396.txt, 2.4.2, the url should already be encoded when combining each component into a full url, and once the url gets to server, the server will split the components and then do a decoding on each component.

So if the url path expected by backend is myserver/my file.txt the encoded url should be http://myserver/my%20file.txt. If the backend path is myserve/my%20file.txt, then the encoded url should be http://myserve/my%2520file.txt. As the server side will decode the url component only once, so the browser should only encode once on the reserved chars.

Although ideally the url should already encode each component when it is generated, but this may not always be true, particularly when user input url from browser address bar. So the browser handles this case by checking the url and if it finds any invalid chars (like space) in the url, then it will assume the url is not yet encoded, and it will encode the special char in the url. However, if there is not invalid chars in the url, then it will assume the url is already encoded, and skip this steps.

If user agent other than browser sends the http request to http server, it should follow the same convention as browser, so the server can handle the request in the same way without concerning which user agent sends the requests.


This also applies to file url scheme of file:///