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:///

Tuesday, January 24, 2017

How to get the content of android apk file on mac

The easiest way to get the content of android apk file without any third party tools, is using unzip command line from mac's terminal app.

Just go to the folder containing the apk file from the terminal app, and run the below command
unzip androidapkfilename.apk -d targetfoldername

Android studio also has a menu item of "Build->Analyze APK...", but it cannot extract the content of APK file to local folder, so it is less useful.


Monday, January 16, 2017

Create WCF oData service on Window 10's IIS with Visual Studio 2015

To create odata service using WCF with Visual Studio 2015 on Windows 10 IIS, the first steps is following the instruction of 
https://www.codeproject.com/articles/1087982/create-a-wcf-dataservice-in-visual-studio
to create a oData WCF service. But the above link does not have a good description about how to publish the oData service to local IIS. 

To do that, following the instruction of 
https://debugmode.net/2014/06/18/publish-wcf-service-in-local-iis-on-visual-studio-2013/
The same steps also works on Visual Studio 2015.

Then for Windows 10 IIS, you will need to using the Turn Windows Feature on or off tool to turn on the below items:
1. Under Internet Information Services/World Wide Web Services\Application Development Features, turn on all items exception CGI
2.Undre .Net Framework 4.6 Advanced Services\WCF Services, turn on Http Activation item.

Then run the WCF project with the service url as below sample will get the service description
http://localhost/wcfodata/WcfDataService.svc/

Monday, January 9, 2017

How to set ios navigation view control animation direction to left/right or up/down

By default when using ios navigation view controller to manage several UIViewController using show segue, the default animation is left/right. However, in certain cases, it may make sense to change the animation direction to up/down.

It is possible to implement a custom UIStoryboardSegue to do so as mentioned in http://stackoverflow.com/questions/30763519/ios-segue-left-to-right

However, an easy way to change navigation animation direction from left/right to up/down is embedded each uiviewcontroller into its own UINavigationViewController, and you will get the up/down animation automatically. Note you will need to manually handle the back button on each UIViewController's navigation bar, so that it will unwind to the previous screen.

By the way, when using a single navigation view controller to manage several view controllers, you can only customize the navigation bar buttons title for the first view controller on storyboard, but not be able to do so for other view controllers. In order to customize the navigation bar items for other UIViewControllers from storyboard, you will need to drag and drop a navigation item (not navigation bar, as navigation bar is only for adding into UINavigationViewController) into the UIViewController from storyboard.

How cordova UIWebview passes javascript exec call into native code

When UIWebView is used, iosExec js method in cordova.js will be called by a cordova js bridge call.
The pokeNative method will be called to create an iframe element with the src of "gap://ready". When an iframe is added into the DOM tree, it will invoke the native side code in CDVUIWebViewDelegate.m method

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
Which will check whether the scheme is "gap", if so the native code will start to handle the bridge call.