Sunday, June 14, 2015

iOS RunLoop, NSURLConnection, NSURLSession

iOS provides several ways to execute task in background thread,  like operation queue or Grand Central Dispatch, usually it is not required to fully understand ios RunLoop to make the function work, but in more complex cases, understanding RunLoop is necessary to make things work as expected.

iOS runloop is similar to Windows EventLoop, its main purpose is keeping the thread from exit when it is still needed, but only actively handle the events when the events are arrived.

For the main thread, the ios system will create a RunLoop automatically after starting the app, so the application can keep running, you can attach a runloop observer to the MainRunLoop from AppDidFinishLoadWithOption to verify it using the below method. Whenever any touch event happens on the screen, the Runloop Observer will be noticed for the event.


+ (void) addRunloopObserver:(NSRunLoop*)runloop {
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopAllActivities, YES, 0,
            ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity){
                                                                       static unsigned long count = 0;
                                                                       NSString* strAct;
                                                                       if (activity == kCFRunLoopEntry){
                                                                           strAct = @"kCFRunLoopEntry";
                                                                       }
                                                                       else if ( activity == kCFRunLoopBeforeTimers){
                                                                           strAct =@"kCFRunLoopBeforeTimers";
                                                                       }
                                                                       else if ( activity == kCFRunLoopBeforeSources ){
                                                                           strAct =@"kCFRunLoopBeforeSources";
                                                                       }
                                                                       else if (activity ==  kCFRunLoopBeforeWaiting  ) {
                                                                           strAct =@"kCFRunLoopBeforeWaiting";
                                                                       }
                                                                       else if (activity == kCFRunLoopAfterWaiting){
                                                                           strAct =@"kCFRunLoopAfterWaiting";
                                                                       }
                                                                       else if (activity == kCFRunLoopExit){
                                                                           strAct =@"kCFRunLoopExit";
                                                                       }
                                                                       else {
                                                                           strAct =@"unknown";
                                                                       }
                                                                                                                                            
                                                                       NSLog(@"activity %lu: %@", ++count, strAct);
                                                                   });
CFRunLoopRef loop = [runloop getCFRunLoop];
CFRunLoopAddObserver(loop, observer, kCFRunLoopCommonModes);

}

But if other cases related to the background thread, you will need to start the runloop listener by yourself if you need to keep the thread alive to handle the runLoop event. 

Note when you call [NSRunLoop currentRunLoop], it will create and return a RunLoop object if not existing, so have a RunLoop object for the current thread does not mean the RunLoop has been started to listen for the RunLoop event, and or when or under what condition the RunLoop will stop listening the RunLoop event, and exit the thread method. 

Pay attention when starting while loop using NSRunloop's Run:untilData or beforeDate method, there must be at least one event source already scheduled into the runloop, and the event source's runmode must include the currnt runloop's runmode, this will make the runloop method to believe it need to wait for something form the event source. Otherwise, the runloop sees there is no any event source is scheduled, and it will immediately exit and enter the Run method again to create a dead loop. Add a NSTimer or NSUrlConnection will make the runloop wait for the signal of the event. A good practice is adding the event source in NSRunLoopCommonModes, and start the RunLoop's run method in NSDefaultRunLoopMode

NSThread:
If the background task is started by NSThread's InitWithTask method, then the RunLoop listener is not started. For example, if you start a NSTimer and add it to the new thread's current RunLoop, the timer event will never fires, as the thread method will immediately exit. In order to receive the Timer event, the thread method has to start a while loop to keep listen the runloop events and also prevent the thread from exiting, until all the thread works are finished. 

  while (shouldKeepRunning){
        [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        NSLog(@"NSLoop returned : %@", [NSDate date]);
    }

Similarly, if you starts a NSURLConnection and schedule it in the thread's runLoop as below, then the connection data delegate method will never be called as the thread already exits without holding by the Runloop's while loop. Add the above while loop in the thread method will get the response. Once the connectionDidFinishLoading is called the nsUrlConnection will remove itself from the RunLoop event source automatically.
    conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [conn scheduleInRunLoop:threadRunloop forMode:NSRunLoopCommonModes];
    [conn start];


NSOperationQueue
NSOperationQueue for async operation is similar to NSThread, if an operation is scheduled in a background thread by NSOperationQueue, then the NSRunLoop is not started in the worker thread.  For exmple, the NSTimer added into the operation block thread's RunLoop will not get its timer event fired.

NSURLConnection uses NSOperationQueue as alternative for RunLoop. There are two different ways to use it for NSUrlConnection. When NSURLConnection sendAsynchronousRequest method is used, it uses a block to get the response, but the method does not take a delegate parameter, so the caller cannot handle authentication or connection status event. . 
+ (void)sendAsynchronousRequest:(NSURLRequest*) request     queue:(NSOperationQueue*) queue   completionHandler:);

In order to handle authentication request and other connection delegate event with NSOperation for NSURLConnection, setDelegateQueue method is needed. The connection delegate method will be called each time on a thread method, and there is no need to use RunLoop to drive the connection event.


GCD (Grant Central Dispatch)
GCD has the same behavior as NSOperationQueue when handle async dispatch, actually NSOperation uses GCD internally, so it is expected to have the same behavior as NSOperationQueue.

However, the dispatch main queue is unique, as it needs to communicate with the existing main thread. As the thread already exists and is running, the only way to let the thread executes a block or method is injecting an event into its NSRunLoop. That is why handling the main queue or main NSOperationQueue, the task is posted into the application's main RunLoop to be executed, the task is added with mode of NSRunLoopCommonModes.


NSURLSession
Unlike NSURLConnection, NUURLSession no longer gives developer the option to use a RunLoop to handle authentication and delegate event. So developers do not need to worry about whether the RunLoop has been created and started, whether the Runloop modes match the event source, whether the RunLoop thread has exited. That is a major difference between NSURLConnection and NSURLSession. Code using NSURLConnection scheduleInRunLoop method definitely should be updated to use NSOperationQueue to handle the delegate event.

NSObject
Most [NSObject performSelector ...] posts the selector into the thread's NSRunLoop, so the target thread needs to have a NSRunLoop already started and listen for event source to work. Those methods can be used when you need to communicate with an already started long last thread, the common way to do so is the target thread starts an NSRunLoop, and caller can posting a event into its RunLoop listener to run a task, besides the application's main thread, another example is UIWebView's NSURLProtocol client thread.

[NSObject performSelectorInBackground...] method is different from other performSelector method, it just creates an background thread and runs the selector without a NSRunLoop in the target thread.

Sunday, June 7, 2015

What is tint color in ios

Usually, ui control uses color and background color to define colors used to show the control. But for buttonor other clickable control, it may be helpful to use the same color schema to indicate whether they are clickable or not at the current screen. That is what tint color property is used in iOS UIView and sub controls.

iOS 7 has introduced the tintColor property on UIView . It is used to visually indicate which controls on the screen are active or have an action associated with them. For example, bar button items and tab bar items use tint color by default. If a view control does not have an explicit tint color, it inherits its superview’s tint color, which will indicated by the Default value for the color property shown in the Main Storyboard.

Note the tint color should only applied to the clickable controls when they are enabled, so if the control is disabled,  it should use a different color to show the effect. Button control has a shadow property to do so, but segment control or slide control still show the tint color when they are disabled, it is better to change the color to a different one to give user a better experience. In addition, if a control is not clickable, like a text field, or a title bar, then it should not show with the same color used by the tint color on the screen to avoid confusion.

Friday, June 5, 2015

When external parameter name is needed to call swift functions

In the following cases, the parameter name is required when calling a swift function

1. when defining a regular function without external parameter name, the caller does not specify the first parameter's name, but need to specify all following parameter's name
    func join0(string: String, toString: String, withJoiner: String) -> String {
        return string + toString + withJoiner

    }

    join0("hello", toString: "world", withJoiner: ", ")


2. if the function's first as well as other parameters have external names, (internal name and external name are indicated by a space),
    func join1(string s1: String, toString s2: String, withJoiner: String) -> String {
        return s1 + withJoiner + s2
    }
   
    join1(string: "hello", toString: "world", withJoiner: ", ")

3. if the function's first parameters starts with '#', it indicates internal name and external name are same
    func join2(#stringStringtoStringStringwithJoinerString) -> String {
        return s1 + joiner + s2
    }
   
    join1(string"hello"toString"world"withJoiner", ")
 
4. if the function's first parameters has default value, the caller must specify the parameter name to call it
    func join3(stringString ="abc"toStringStringwithJoinerString) -> String {
        return s1 + joiner + s2
    }
   
    join3(string: "hello", toString: "world", withJoiner: ", ") 

 5.In any case, if the parameter's external name is '_', then caller does not need specify the parameter's name,

    func join4( _ string: String = "abc", _ toString: String, _ withJoiner: String) -> String {
        return string + withJoiner + toString
    }
    
       join4("hello", "world", ", ")

    Sometimes, if a parameter has a default value, but does not want to have an external name, then, explicitly specify '_' as external parameter name as shown below:
  • func join(s1Strings2String, _ joinerString = " ") -> String {
  • return s1 + joiner + s2}
  • join("hello""world""-")

Saturday, May 30, 2015

Swift syntax for closure and generic alertbox function

Closure in swift is corresponding to block in Objective C, but its syntax is quite different from block in objective C, or other languages. One thing to notice is closure syntax in swift is quite similar to function definition, but without parameter name.

For example, the beginBackgroundTaskWithName defined in UIApplication class has closure parameter handler as shown below, which takes no parameter () and return Void. In addition, the closure can be nil indicated by the optional type flag ?.

func beginBackgroundTaskWithName(_ taskName: String?,
               expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier

A sample code to call the method may looks like:
    var bgTask :UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
     bgTask = application.beginBackgroundTaskWithName("logout", expirationHandler: { () in
            ...
            application.endBackgroundTask(bgTask)
            bgTask = UIBackgroundTaskInvalid;
     });

Now, let take UIAlertAction as a sample to create a generic alertbox. The constructor of UIAlertAction takes a closure parameter handler, which takes a UIAlertAction parameter and return Void. the handler is not optional indicated by !
UIAlertAction(title title: Stringstyle style: UIAlertActionStylehandler handler: ((UIAlertAction!) -> Void )!)

Let see how to create a generic alertbox that takes a title, message, buttonTitle and handler parameters, and once ok is clicked, it will call the handler parameter passed to it

func alert(title: String, message: String, buttonTitle: String, handler:((UIAlertAction!) -> Void )!){             var alertDlg : UIAlertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    
        var okAction : UIAlertAction = UIAlertAction(title: buttonTitle, style: UIAlertActionStyle.Default, handler:handler);
                    
         alertDlg.addAction(okAction)
         self.presentViewController(alertDlg, animated: false, completion: nil)
 }

A sample code to call the method looks like
self.alert("Warning", message: "The passcode is not correct, please try again", buttonTitle: "Ok",            handler: {(alert) in    println("ok pressed")})


Thursday, May 14, 2015

Debug the first line of js code when page loads

Similar to debug cordova app when app starts, the similar approach can also be used to debug general javascript html page. 

Assume when page starts, it runs the below method, and you need to attach debugger on it.
       
var startFunc = function(){
           console.log("my start func");
}

startFunc();


Then adding the delayedStartForDebug method and also call  delayedStartForDebug(startFunc); will give you enough time to attach the debugger

var startFunc = function(){
           console.log("my start func");
}

var delayedStartForDebug = function(func) {
        var originThis = this;
        var bWait = true;
        var bDone =false;
        var i = 0
        var timer = setInterval(
                function(){
                    if (bWait && i < 10 ){
                        i++;
                        console.log("set bWait to false from console window to continue");
                    }
                    else{
                        if (!bDone){
                            bDone = true;
                            clearInterval(timer);
                            func.apply(originThis, arguments);
                        }
                    }
             }, 3000);
   }
   
   //startFunc();

   delayedStartForDebug(startFunc);

Thursday, May 7, 2015

Beautify javascript file with npm js-beautify

npm package can be used to beautify the javascript file for easy read and debug.

To do so, first download js-beautify package by run:
npm -g install js-beautify

then run the follow command to beautify it
js-beautify oldfile.js -o newfile.js

The new file will be beautified with proper indent.

If you use xcode for most of your work, then it is a good idea to install the xcode plugin for js-beautify. The detailed information is available at
https://github.com/bumaociyuan/JSFormatter-Xcode

oAuth and SMAL authentication

SAML and oAuth are two popular authentication methods used in web application, but they are for different purposes.

SAML is more similar to other authentication methods, such as basic, client cert, etc., in which, the web server must manage user's authorization information, and decide which user can perform what kind of operation. The major benefit of SAML over other authentication methods are Single-Sign-On, so that a independent identity provider can handle authentication request for multiple service providers. The key thing is although different service providers cannot share their authentication session with each other, the authentication session in the common identity provider can be used by all service providers, so as long as user has a valid authentication session on the SAML identity provider, then multiple service provider can reuse it without requiring user to authenticate again.

oAuth, on other hand, is quite different. When a web server uses oAuth, it no longer manager each user or user group's information, instead, it relies on the oauth provider's instruction on what operation can be performed on a oauth session. The oauth provider will authenticate the user and also authorization user' permission. As web server no longer manages user's information, so it does not need to expose the user credential to web server. Note oauth provider still needs to authenticate the user, using SAML, Basic, or client certificate. So it moves both authentication and authorization task out of the web server to third party oauth provider.

When oauth provider returns an oauth access token to web server, the web server must decide whether to allow the session to access the required web resource, this means there is a close coupling between the oauth authorization provider and web server. That is why, usually oauth provider from one company only support the web service from the same company. While on the other hand, SAML provider from one company can be easily used in other company's web service.

As oAuth can use SAML for user authentication, so SSO benefit from SAML authentication can also be used by oAuth. If user is already authenticated with the SAML IDP, the SAML session can also be used by any SAML service provider powered oauth web server, so the oauth provider can directly return an access token without requiring user to input his credential on the oAuth login page.