Monday 17 March 2014

HTML 5 Local Storage with iOS app.

It all starts with the Concept of SSO(Single Sign On):

The concept is simple- If there are a set of different apps which performs validation -while logging in- from the same server ,then if the validation is done by the server once there is no need to validate the end user again for any other app that uses the same validating server(for a predefined time period, after the time period elapses the validation has to be done again) .How does one ensure that validation has been done once by the server? Simple - once successfully validated, the server returns a token which is saved by the app in a persistent store. This persistent store has to be common so that the other apps can access this store and if the token is present in the store the auto validation occurs.

Can Keychain be used as the required persistent store?
   
No. The iOS keychain allows us to share data between applications that share a common app -id prefix.The apps in question may or may not have the common app -id prefix.Thus this is not a very optimal solution.However if the apps in question are guaranteed to use a same app - id keychain can be used.

Alternate Solution:

HTML- 5 Local Storage:

Definition -
 Simply put, it’s a way for web pages to store named key/value pairs locally, within the client web browser. Like cookies, this data persists even after you navigate away from the web site, close your browser tab, exit your browser, or what have you. Unlike cookies, this data is never transmitted to the remote web server (unless you go out of your way to send it manually).
                                                                                                        - Mark Pilgrim (Dive into HTML 5)

Mechanism to save Data in HTML5 Local Storage:

The working of SafariStore(iOS app):

The SafariStore app is designed to work by intercepting the Ajax calls made by a url shown loaded in a UIWebView. The interceptor logic works by employing a javaScript injection that captures the Ajax requests before they hit the server. 
Once the Ajax calls are caught hold of, the SafariStore monitors the response of a Ajax call which returns a response with the header OBSSOTOKEN and JSESSIONID. These are key value pairs which are separated from the header and they are loaded in the local Storage of the Safari Browser.



Code for Ajax capturing - ajax_handler.js file :

var s_ajaxListener = new Object();
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
s_ajaxListener.callback = function () {
      window.location  = 'mpajaxhandler://' + this.url;
}

XMLHttpRequest.prototype.open = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempOpen.apply(this, arguments);
  s_ajaxListener.method = a;  
  s_ajaxListener.url = b;
  if (a.toLowerCase() == 'get') {
    s_ajaxListener.data = b.split('?');
    s_ajaxListener.data = s_ajaxListener.data[1];
  }
}

XMLHttpRequest.prototype.send = function(a,b) {
  if (!a) var a='';
  if (!b) var b='';
  s_ajaxListener.tempSend.apply(this, arguments);
  if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
  s_ajaxListener.callback();
}

Script Injection when Page loads:

+ (void)initialize {
    JSHandler = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"ajax_handler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:nil] ;
 }

- (void)webViewDidStartLoad:(UIWebView *)webView {
    //Injecting the javaScript before the view starts to load.Employing Script Injection
      [webUIView stringByEvaluatingJavaScriptFromString:JSHandler];
   
}

Safari Local Storage with SafariStore:



The OBSSOToken so collected is sent to a scripting file(.js) which handles the saving of the token in safari local cache this makes calling Safari from the SafariStore essential. This is achieved through an HTML file saved in the  local recourses of Xcode which calls the above mentioned scripting file.This HTML file is loaded in the Safari browser when safari is invoked from the SafariStore.This makes sure that the OBSSOTOKEN is stored in Safari and not locally on a UIWebView.



Code for the .js file:

var result =function (tokenValue) {
    //if browser does not support this feature give a error code
    if (typeof (localStorage) == 'undefined') {
        return 404;
    }
    else {
        try {
            localStorage.setItem("ssoToken", tokenValue); //Set the key-value pair
            if (localStorage.getItem("ssoToken")) {
                //Get the value from storage area
                var output = '';
                output = localStorage.getItem("ssoToken");
                return output;
            }
        }
        catch (e) {
            //if the memory is full show a error code
            if (e == QUOTA_EXCEEDED_ERR) {
                return 400;
            }
        }
    }
    
};

Code for HTML file calling the .js file:

<!DOCTYPE html>
<html>
    <body>
        <script src="localStorageScriptHandler.js"></script>
    </body>
</html>

Challenges in the above logical flow:



While the above logic works flawlessly when executed in a simulator.The logic fails while executing on an iOS device.The point of failure is when we try to load the local HTML file in a Safari browser from the app. The reason for this failure is due to Apple disallowing any app from running a local scripting file in Safari due to security reasons.
The following link from Apple communities discuss this:https://discussions.apple.com/thread/2206391?start=0&tstart=0



Solution and Workaround:



While Apple stops an app from running a local scripting file in Safari it however does not stop loading a fully qualified web URL from the app into the safari browser.This brings us to a solution that if we are able to host our HTML file and the scripting file in a secured-server which can be reached from our app,we will be able to achieve the desired result while working with an iOS device.Practically we managed to create an app which uses DropBox as a hosting server for the HTML file needed to set the local storage in mobile safari browser and then another app using the same  hosting server managed to retrieve the value of the local storage from Safari.



SideEffects:



The only side effect to the above mentioned solution would be a bad user experience because a user sees a Safari-browser being launched suddenly from the  app.
   
Result:

The Final result is awesome. We connect the iOS device that we have been using to our MAC and launch safari. We launch the app from the spring board and the test- url we loaded in the web view comes up. Now we goto the develop option in our MAC safari and we find the name of our app listed ,on clicking it we have a  web - inspector pane where we can actually see the OBSSOTOKEN and JSESSIONID stored in local Storage as a key-value pair.


Saturday 8 March 2014

Writing and Coding:

There is a peculiar comfort in arranging instructions till it makes logical sense and gets a particular work done. It's like solving a  jig-saw puzzle or a sudoku. There is a feeling of elation, of mental satisfaction which comes whenever the logical pattern created by our brain matches with the understanding of the computer. The first code I wrote in my life(I was in the fourth grade then) was in basic- a simple addition of two numbers,when the computer finally produced an output of the sum of two integers ,I had the feeling-"Dear God, I have the ability to think like a machine".
  
There is another occupation in life that matches this elation of coding.It's writing.It's just like coding, arranging particular words in a sentence so that it makes sense to another individual.The clarity of thought is brought out in both cases. A good coder is the one whose goals are set and the path to that goal is clearly thought out similarly a good writer expresses his thoughts clearly in a logical order to his readers and reaches his/her own conclusions.The beauty of both the arts come from the simplicity of thought which achieves the most direct method of solving a problem.A good coder gets his/her work done by the minimum intricacy possible similarly  a good writing is simple,direct and concise.There is a remarkable absence of intricacy, ornamentation and clutter in both good writing and good coding.Now here's a thing that intrigues me most - Are all good coders good writers and vice versa.I am still working on that.