Wednesday, November 18, 2015

NSURLSession delegate and data task delegate for CustomHttpProtocol sample

Apple provides a sample project CustomHttpProtocol to demonstrate how NSURLProtocol works with NSURLSession.

Note the project includes a file QNSURLSessionDemux, which involves how NSURLSession' delegate works.

When creating a NSURLSession object from NSURLSessionConfiguration,
+ sessionWithConfiguration:delegate:delegateQueue:
a delegate of NSURLSessionDelegate can be specified in the delegate parameter. Apparently this delegate will be used to handle NSURLSessionDelegate event.

When creating the data task from the NSURLSession object, Apple defiends NSURLSessionDataDelegate and NSURLSessionTaskDelegate to handle the per task event. However, unlike NSURLSession, when creating the data task, there is no parameter in
- dataTaskWithRequest:completionHandler:
method to specify a delegate to handle the per task event. Instead the delegate object passed for creating NSURLSession will also be used to handle the per task delegate event.

Then the problem comes, as the delegate for NSURLSession is a shared delegate object for all data tasks, but each task has its own logic to handle the response data as well as mode and thread, so once the NSURLSession's common delegate get the per task delegate event, it has to find a way to route back to the per task delegate to handle it.

QNSURLSessionDemux in CustomHttpProtocol is created for this purpose, it keeps a dictionary of per task data delegate specified by application when the data task is created. Then the NSURLSessionDelegate will route the per task event their corresponding per task delegate.

Actually, NSURLSessionTaskDelegate and NSURLSessionDataDelegate are all derived from NSURLSessionDelegate, which implies the NSURLSession delegate will also be used to handle per task event.

One last note, when creating the data task, if custom delegate will be used to handle NSURLSession's data task event, then use the method that does not take the completionHandler. Otherwise, if completionHandler exists, then the default system delegate will be used, and custom delegate will not be invoked.  

SAP UI5: Logic to start a tile from launchpad

Shell-dbg.controller.js openApp() method is called, which then calls NavContainer-dbg.js to method.
Render manger will call ApplicationContainer-dbg.js createUI5Component method

The UI5 application's component.js createContent method is called, this is the first chance the application's code is executed. The method will create the view.
When creating the view, the application's Main.Controller.js' onInit method is called.

Application-dbg.js main() method is called, which calls ApplicationImplementation-dbg.js startApplication method.

ConnectionManger-dbg.js getNewInstance method is called, which will call initModels() for creating oDataModel based on url and application config (line 171)

oDataModel-dbg.js oDataModel constructor is called to create the model, which will get metadata from server.

The UI5 application view's onInit method is called, which will all getServiceSchemeVersion()

Monday, November 2, 2015

Config proxy for Android emulator for internet access

When running android emulator on a Windows box, if the Windows box is behind a proxy, then the android emulator will also need to config the proxy settings in order to get internet access. This can be easily verified by using mobile chrome browser to access www.google.com, if it fail, then likely it is due to proxy settings. 

The below steps can be used for this:
1. From Android emulator, open settings app
2. Under Wireless & Networks section, click More...
3. click Mobile network item
4. click Access Point Names item
5. click T-Mobile US item
6. Set proxy to your proxy server's name, for example: proxy.phl.sap.corp
7. Set port to your proxy server's port, for example: 8080
8. Go back and open mobile browser, and visit http://www.google.com and it should work

Friday, October 30, 2015

Fix Mac boot hanging due to missing var/folders

Although /var/folders" (or "/private/var/folders") is "per-user temporary files and caches", and its content can be cleaned by system at any time, but the var/folders itself cannot be deleted. After I accidentally deleted var/folders fold from my mac, and the box could not reboot successfully.  

By restarting the mac in single user mode (pressing Command+S) in unix shell, I tried to add the var/folders back, however, the mkdir failed as the folder is mounted as readonly. Unfortunately, chflags does not work in this case.

The rescue is
mount -uw

which will mount the folder as read-write. After that, mkdir can successfully create the var/folders again, and the mac can reboot properly. The strange thing is you can delete var/folders using Finder, but you cannot put it back or create a new var/folders folder using Finder.

Sunday, October 18, 2015

Android studio build issues- gradle proxy, file path size, etc

1. When running android studio on Windows, the gradle build may get an error of "No Resource found that matches ..." or "unable to open file ...". 

One possible reason of the error is due to the limitation of the maximal length of the file path. To fix the issue, move the project close to a root folder of the current drive, instead of staying in a deep nested subfolder on the current drive.


2. The android studio only build armv7 apk when running the app on emulator
In Build.gradle, locate productFlavors item and remove the "armv7" element


3. set proxy for gradle
create a text file with name of gradle.properties and put the file under the project's root folder with the content of:

systemProp.http.proxyHost=proxy.phl.sap.corp
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=proxy.phl.sap.corp
systemProp.https.proxyPort=8080


4. for the error of "Unable to execute dex: method ID not in [0, 0xffff]"

You need to enable multiDex with the below steps:
First, update android defaultConfig
android {
   defaultConfig {
      ...
      multiDexEnabled = true
   }
}

Then, add multiple dependency
dependencies {
  ...
  compile 'com.android.support:multidex:1.0.0'


5. for the out of memory GC error
Add the below options in Android block
dexOptions {
    incremental true
    javaMaxHeapSize "4g"
}

Friday, October 16, 2015

Show and copy android screen from device to mac

1.download droidAtScreen-1.2.jar from http://droid-at-screen.org/
2.connect the android device with mac and Configure your device to allow USB Debugging from (Settings-> Developer options -> USB debugging)
3.from command line, run
java -jar droidAtScreen-a.b.c.jar

Monday, October 5, 2015

How to move android emulator screen on Windows 8

1. select android emulator screen by mouse
2. press ALT+SPACE
3. while the mouse is shown as + sign, select Move menu
4. release mouse button, and use up, down or left, right key to move emulator screen
5. after press any direction key, then you can also use mouse to move the emulator screen

Sunday, September 27, 2015

Exclude items from iOS backup and restore

1. File and folder
application can apply property NSURLIsExcludedFromBackupKey to exclude file or folder item from itune/icloud backup and restore.

 NSURL* URL= [NSURL fileURLWithPath: filePathString];

 NSError *error = nil;
 BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                  forKey: NSURLIsExcludedFromBackupKey error: &error];


2. NSUserDefault
NSUserDefault items are always included in the backup and restore and cannot be excluded.


3. KeyChain
The following three attributes can be used to prevent the keychain items be restored to other devices. The values can be set as kSecAttrAccessible attribute in the secItemAdd method

Sample:
[dict setObject:kSecAttrAccessibleAlwaysThisDeviceOnly forKey:kSecAttrAccessible];

kSecAttrAccessibleWhenUnlockedThisDeviceOnly
Keychain item is accessible only after the device is unlocked and the item cannot be migrated between devices.
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
Keychain item is accessible after the first unlock of the device and the item cannot be migrated between devices.
kSecAttrAccessibleAlwaysThisDeviceOnly
Keychain item is accessible even the device is locked and the item cannot be migrated between devices.

Wednesday, September 23, 2015

Share and record ios screen on mac with QuickTime Player

1. connect ios device with mac using usb cable
2. open QuickTime Player app from mac, click done button on the dialog prompted by QuickTime player.
3 click QuickTime Player's File->"New Movie Recording" menu, it should show you a recording config screen.
4 Move the cursor into the config screen, and select the down arrow on left of the red dot and select your device

5.The device screen should show on mac screen, you can also record the device screen by clicking on the red dot to start. Click it again will stop the recording.

Monday, September 21, 2015

Cross-Site Request forgery (CSRF) and Corss-origin-Resource-Share (CORS)

As mentioned in http://jonathanblog2000.blogspot.ca/2015/09/http-cross-origin-request-and-http.html, CORS is for preventing malicious javascript code to access response returned from a different domain, so even if user are tricked to open a malicious web page, it cannot load the response returned from user's real server. This is mostly implemented by enforcing the "Access-Control-Allow-Origin" header on client side.

Cross Site Request Forgery (CSRF), on other hand, is for a different kind of attack, it leverages the fact that browser automatically sends the cookie including authentication cookie with the request to the same server, so if the server based on the authentication cookie to validate the request, then the server will accept the malicious request as a valid one. In this case, the malicious request is sent from the same browser instance triggered by a link in an email or a different web page, CORS will not prevent this kind of attack, as the attacker does not need to get the response for his purpose. As long as the request is processed by server, then the attach is achieved.

To avoid the CSRF attack, the server needs to use something to validate the client request other than session cookie, for example, a http header for CSRF token returned from server will be a good choice. The idea is when the app sends any update (POST, PATCH) request to server, this CSRF header needs to be included by javascript code, so the server can valid the request by checking this particular header.  The request will be executed only if the header is correct.

Note, although malicious code can trick user to send a Get requests to the real server, but it cannot get or parse the server response to find the CSRF token from the response, as the malicious js code is loaded from a different domain, and CORS limitation will block it to load the response returned from the real server. So the malicious code can never send a post request with the CSRF token in it to pass the server side check.
  

HTTP Cross Origin request and HTTP access control (CORS)

For http content loaded from one domain (protocol, host, port), it may have link to request from a different domain, such as an image, a css stylesheet or script. This is very common usage.

However, when using a script to send a request to a different domain, it will expose a security risk as unlike web link, malicious script code can inject request to server without user's acknowledge.

To avoid sending cross origin request from script, browser on client side enforces HTTP access control (CORS) based on server response. The server response includes the header of
Access-Control-Allow-Origin: *
which indicates the response can be accessed by which domain in a cross-site manner. The Access-Control-Allow-Origin header should always include the request's Origin header to allow the original web page to access the response, but it can also add another domain into it, or set * to allow all domains to access it.

Note although the server returns the Access-Control-Allow-Origin header to help client to enforce CORS control, it is the browser (not the server) that checks and enforce the restriction. The server will always return the requested resource to client regardless the request data.


Sunday, September 20, 2015

Cordova core plugin list

When creating a cordova project, it just adds the core function to bridge the javascript (cordova.js) API to the native API. And you need to add the cordova core plugins to access native operating system functionality such as the accelerometer, camera, compass, the file system and others. The core plugins are developed by the Apache Cordova team, and are updated and improved with each new version.

You can also add other third party plugins registered at http://plugins.cordova.io/npm/index.html to your project, but you need to be careful about the quality of the third party plugins, unless they are developed by yourself. So you should always check the core plugins first before looking at third party plugins or implementing a new plugin by yourself.

When searching at http://plugins.cordova.io/npm/index.html, the core plugins has a blue bar at the left as shown below

To make it easy the full core plugin list is added below:

cordova-plugin-battery-status
cordova-plugin-camera
cordova-plugin-console
cordova-plugin-contacts
cordova-plugin-device
cordova-plugin-device-motion
cordova-plugin-device-orientation
cordova-plugin-dialogs
cordova-plugin-file
cordova-plugin-file-transfer
cordova-plugin-geolocation
cordova-plugin-globalization
cordova-plugin-inappbrowser
cordova-plugin-media
cordova-plugin-media-capture
cordova-plugin-network-information
cordova-plugin-splashscreen
cordova-plugin-statusbar
cordova-plugin-vibration
cordova-plugin-whitelist  

Friday, September 11, 2015

Fail to empty trash folder on mac

Sometimes the trash folder is unable to empty for some reason. If it happens, using the below methods to empty it
1. click opt and shift key and right click the trash icon and then select secure empty trash
2. if that does not work, then open terminal, and type
chflags -R nouchg 
with a space after it. Then open trash folder in Finder and select all files and drag&drop files to the terminal to unlock the files
3. Repeat the step 1 again to empty the trash

Sunday, August 30, 2015

Start http web server for static page on mac

Certain web static content (like open ui5 sdk) on local disk must be served from a web server, instead of directly opening from the local file system.

On Mac, once the static web content is available on a local folder, it can be simply started from a terminal.

1. First open terminal app, and navigate to the folder contains the static web content.

2. Run the below comment with the port number as the last parameter
python -m SimpleHTTPServer 8000

3.start the browser, and visit the below url
http://localhost:8000/yourHtmlPage.html

Tuesday, August 4, 2015

Delay javascript starting code for debug when loading html page

1. Include the below js file in the same folder of html file and also include the js file in html file's script element.
The file is also available at  https://github.com/jonathanli2/delaystart

//delaystart.js

var originalFunc;
var waitPeriod = 30;
function setDelayAt(obj, prop, waitSeconds){
originalFunc = obj[prop];
obj[prop] = delayedStartForDebug;
if (waitSeconds){
   waitPeriod = waitSeconds;
}
}

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

2. find a method that is used to start the application logic,  just before calling this starting method, add the below code
    setDelayAt(obj, "startingMethodName", 30);
The first parameter is the object that contains the start method, the second parameter is the start method name. The third parameter is optional for seconds to be delayed. 

Monday, August 3, 2015

Problem with javascript xhook, and an alternative for it - xhrhook

xhook is a javascript library that is used to intercept xmlhttprequests, and manipulate the request and response.

However, it needs cautions to use xhook in your javascript project, as it may cause errors if your project customizes xmlhttprequest object. For example, after including xhook.js in your html file,  calling the below code returns undefined instead of a function object as expected.

XMLHttpRequest.prototype.open 

The problem is, xhook uses an proxy approach to override the xmlhttprequest's behavior. Internally xhook overrides the xmlhttprequest construct function, and when caller calls

var xhr = new xmlhttprequest(),;

it just create and return a regular javascript object instead of a real xmlhttprequest object, and this regular javascript object has a private member which points to a real xmlhttprequest object. So when caller calls any xmlhttprequest method, the regular javascript object just forwards the call to the internal real xmlhttprequest to do the job.

Using the proxy approach gives xhook the full control of the request and response. However, the issue is although caller thinks he is dealing with a real xmlhttprequest object,  but actually it is not, so when caller talks to this fake xmlhttprequest object, it may fail to return the expected result as a real xmlhttprequest does.

So, as an alternative, if your project only needs to monitor the request and response data, but does not need to modify the response data, (you can still modify the request data), then xhook may not be the best choice. As it is more reliable and safe to return a real xmlhttprequest object to caller when it is requested,  and then just override each individual methods or event listener. Actually many projects already did the similar thing for the open or send method.

To test with this approach, a new xhrhook project has been started in github at

https://github.com/jonathanli2/xhrhook


Currently xhrhook.js only supports monitor open, send method, and onreadystatechanged and onload event listener. But it should be easy to support all methods and event listeners.

Note all event listener may not be set by application until send method is called, so a default send handler is added automatically when loading xhrhook to hook the event listener handler.

Monday, July 27, 2015

Using CFNETWORK_DIAGNOSTICS for network log for iOS

CFNetwork has built-in support to log network activity for iOS device by setting the environment variable CFNETWORK_DIAGNOSTICS.

The CFNETWORK_DIAGNOSTICS can be set to the following values:

    1: Enables internal CFNetwork event and url logging

    2: Adds information about http header and how CFNetwork decides to make and re-use TCP sockets

    3: Adds decrypted bytes in and out of network connection.

Following the below steps to use this feature
1. Enable the feature with one of the following options

   option 1: call setenv method in xcode project's main method
   int main(int argc, char * argv[]) {
       setenv("CFNETWORK_DIAGNOSTICS", "3", 1);
          @autoreleasepool {
               return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
       }
  }

  option 2: set the environment var in project scheme as 


2. build and run the project on device, make the server request to be tested

3. from xcode device tab, export the application container. Then show the package content of the downloaded container from Finder, and the log file will be in the folder of
/AppData/Library/Logs/CrashReporter.

  


Wednesday, July 1, 2015

SAP UI5: bootstrap with sap-ui-core.js and debug tip

sap-ui-core.js
SAP UI5 is started by loading the bootstrap js file sap-ui-core.js. The UI5 document has a tutorial to create a simple app at
https://openui5.hana.ondemand.com/#docs/guide/5b9a170d9f6a4d5784e8ab2ded3d8517.html

<script src="resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.m" data-sap-ui-theme="sap_bluecrystal"</script>

The important thing is data-sap-ui-libs, which tells sap-ui-core.js to load additional UI5 libraries to be used by the application. for example, if you want to create a button from sap.ui.commons library (not sap.m library) using the below code, but does not include "sap.ui.commons" in the library collection, then the call will fail:
btn = new sap.ui.commons.Button(...)
Internally, sap-ui-core.js just calls jquery.sap.require() to load each library specified in this property.

However,  UI5 document does not clearly indicate how it organizes the UI5 library or give a clarr library list for the library information

Debug tips:
0. on Windows, click CTRL+SHIFT+ALT+S to open UI5 diagnostic window

1. As ui5 loads view.js at runtime by xhr request, instead of using the <script> tag, so it cannot attach desktop browser as debugger to those js file. in order to debug the js view code, one option is pre-loading the js file explicitly in html file, and then the file will be ready for debugging as regular js file. In this case, the xhr loader will check and skip the already loaded file.

2. Changing sap-ui-core.js to sap-ui-core-dbg.js in bootstrap script element or setting parameter "sap-ui-debug=true" in the query string will load debug version of sap-ui-core-dbg.js. The first method is recommended as it can be used to debug sap-ui-core functions. it can also be set at bootstrap script tag as
sap-ui-debug="true"
Note, you can also directly load sap-ui-core-dbg.js in bootstrap ui5 script, as that will show the parameter name properly in javascript debugger variable window.

3. Set log level to debug in configuration object to get detailed log
<script src="resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.m" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-config="logLevel:'DEBUG'"</script>

4. By default, all ui5 libraries are loaded synchronously, to load them asynchronously, set  data-sap-ui-preload="sync" in bootstrap script, and then listen for UI5 core's attachInitEvent

<script src="resources/sap-ui-core-dbg.js"
id="sap-ui-bootstrap"
data-sap-ui-config="theme:'sap_bluecrystal',
                    libs:'sap.m',
                    logLevel:'DEBUG',
                    preload:'async'">
</script>
<!-- only load the mobile lib "sap.m" and the "sap_bluecrystal" theme -->

5. click  CTRL + ALT + SHIFT + P to show UI5 technical information dialog box, including setting debug source, and check ui5 version. CTRL + ALT + SHIFT + S to show diagnostic dialog, to see UI5 tree control, property window and binding

Key methods
The bootstrap process uses few key methods in sap-ui-core.js:

sap.ui.define 
Defines a Javascript module with its name, its dependencies and a module value or factory.

sap.ui.require
Resolves one or more module dependencies synchronous (for single parameter) or asynchronously (for callback method)

sap.ui.getCore
Retrieve the SAPUI5 Core instance for the current window.


Get UI5 Version

To check UI5 version from ABAP system, send request to 

http://<HOST>:<port>/sap/public/bc/ui5_ui5/ 
for example:
https://ldai1uia.wdf.sap.corp:44300/sap/public/bc/ui5_ui5 
or 
https://ldai1uia.wdf.sap.corp:44300/sap/public/bc/ui5_ui5/index.html

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")})