Learn NSURLSession using Swift Part 1 - HTTP/HTTPS GET

“In iOS7 and later or OS X v10.9 and later, NSURLSession is the preferred API for new code that performs URL requests” — Apple

Usually, when an iOS developers wants to retrieve contents from certain URL, they choose to use NSURLConnection. In iOS 7 and later, Apple suggest to use NSURLSession instead, NSURLSession is just like an improvement or you can consider as a refactoring, I would like to say it is a “Mutation”.


There are three types of NSURLSession:

a) default sessions: behaviour like NSURLConnection
b) ephemeral sessions: not cache any content to disk
c) download sessions: store the result in file and transferring data even when app is suspended, exits or crashes

Based on above sessions, developers can schedule three types of tasks:

a) data tasks: retrieve data to memory
b) download tasks: download file to disk
c) upload tasks: uploading file from disk and receiving response as data in memory


We can implement a simple HTTP request using Swift Playground:

import Foundation
import XCPlayground
func httpGet(request: NSURLRequest!, callback: (String, String?) -> Void) {
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback(“”, error.localizedDescription)
} else {
var result = NSString(data: data, encoding:
NSASCIIStringEncoding)!
callback(result, nil)
}
}
task.resume()
}
var request = NSMutableURLRequest(URL: NSURL(string: “http://www.google.com")!)
httpGet(request){
(data, error) -> Void in
if error != nil {
println(error)
} else {
println(data)
}
}
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)

In above code section, we use a default session with data task to retrieve Google contents. NSURLSession.sharedSession() returns a singleton session based on default configuration. Then, use this session to create a data task(session.dataTaskWithRequest), bring the URLRequest in and handle the Closures. Finally, execute this task by calling task.resume().

Really simple, isn’t it? What if we wants to retrieve the HTTPS protocol contents?


Let’s try to modify URL in previous code section to “https://www.google.com”.

You will see the results in console like below:

NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9807)

The flow of authentication will choose the handle delegate, if you didn’t implement the session level delegate
(URLSession:didReceiveChallenge:completionHandler:), it will pass to task level delegate(URLSession:task:didReceiveChallenge:completionHandler:).

That means we need to have delegate implementation since the default system delegate didn’t help us to handle these behavior.

class LearnNSURLSession: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate {
typealias CallbackBlock = (result: String, error: String?) -> ()
var callback: CallbackBlock = {
(resultString, error) -> Void in
if error == nil {
println(resultString)
} else {
println(error)
}
}

func httpGet(request: NSMutableURLRequest!, callback: (String,
String?) -> Void) {
var configuration =
NSURLSessionConfiguration.defaultSessionConfiguration()
var session = NSURLSession(configuration: configuration,
delegate: self,
delegateQueue:NSOperationQueue.mainQueue())
var task = session.dataTaskWithRequest(request){
(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error != nil {
callback(“”, error.localizedDescription)
} else {
var result = NSString(data: data, encoding:
NSASCIIStringEncoding)!
callback(result, nil)
}
}
task.resume()
}
   func URLSession(session: NSURLSession, 
didReceiveChallenge challenge:
NSURLAuthenticationChallenge,
completionHandler:
(NSURLSessionAuthChallengeDisposition,
NSURLCredential!) -> Void) {
completionHandler(
NSURLSessionAuthChallengeDisposition.UseCredential,
NSURLCredential(forTrust:
challenge.protectionSpace.serverTrust))
}

func URLSession(session: NSURLSession,
task: NSURLSessionTask,
willPerformHTTPRedirection response:
NSHTTPURLResponse,
newRequest request: NSURLRequest,
completionHandler: (NSURLRequest!) -> Void) {
var newRequest : NSURLRequest? = request
println(newRequest?.description);
completionHandler(newRequest)
}
}
var learn = LearnNSURLSession()
var request = NSMutableURLRequest(URL: NSURL(string: “
https://www.google.com")!)
learn.httpGet(request) {
(resultString, error) -> Void in
learn.callback(result: resultString, error: error)
}

Inside LearnNSURLSession class we inherit the NSObject and implement NSURLSessionDelegate, NSURLSessionTaskDelegate to handle HTTPS authentication and redirection. While executing httpGet with “https://www.google.com”, it will first redirect by server to the final destination, we can handle the redirect manually or just bypass it(in this case we bypass it and handle nothing) through delegate method in NSURLSessionTaskDelegate(URLSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:).

Next, we should implement the didReceiveChallenge for listening challenge request from server. There are many ways to response challenge to server, some may use the username/password or other way to create credential, we just use serverTrust here and pass the NSURLCredential into completion. (URLSession(_:didReceiveChallenge:completionHandler:)

completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust:challenge.protectionSpace.serverTrust))

Now execute it and you can see the results.