Swift: NSURLSession in the Playground (iOS 9.2.1, Swift 2.1, Xcode 7.2)


A basic GET request

You want to retrieve some data from the Internet? This can be done very simply in a Swift Playground.
import Foundation

if let url = NSURL(string:"https://itunes.apple.com/search?term=jack+johnson&limit=5") {
    NSData(contentsOfURL: url)   
}
There are a number of types, including NSData, String (bridged from NSString), NSXMLParser, NSArray, and NSDictionary, that, provided the contents of the URL is appropriate for that type, can be instantiated using the contents of a URL.

It's a great way to get started and to experiment with things like JSON by sending simple GET requests through a URL string, but in order to progress beyond this we need to get to know NSURLSession (available from iOS 7 onwards, although some additional elements were added in iOS 8 and iOS 9).

NSURLSession

The first step in using NSURLSession is to create a session. In a Playground you can get up and running using class method sharedSession.
let session = NSURLSession.sharedSession()
But it comes with the following limitations:
  • You cannot obtain data incrementally as it arrives from the server.
  • You cannot significantly customize the default connection behavior.
  • Your ability to perform authentication is limited.
  • You cannot perform background downloads or uploads while your app is not running.
So it's worth looking at NSURLSessionConfiguration, not only to enable background downloads but also if you want to improve security through the use of an ephemeralSessionConfiguration, because with only an extra few characters of code you are preventing the caching of data and writing of anything unnecessary to disk.
let session = NSURLSession(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration())
Similarly, the benefits of creating a configuration capable of background downloads and uploads are simple to obtain:
let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("my_first_config")
let session = NSURLSession(configuration: config)
There are some rules to be observed here about the relationship between completion handlers, delegates and session configurations, however (see NSURLSession background tasks).

NSURLSession tasks

GET

Now you have the session established you are ready to assign it tasks to complete. We'll start with a GET task.
import Foundation
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

if let url = NSURL(string: "https://itunes.apple.com/search?term=jack+johnson&limit=5") {

    let session = NSURLSession.sharedSession()
    let dataTask = session.dataTaskWithURL(url,completionHandler: {(data,_,_) in
        defer {
            XCPlaygroundPage.currentPage.finishExecution()
        }
        if let data = data, string = String(data: data, encoding: NSUTF8StringEncoding) {
            print(string)
        }
        }
    )
    
    dataTask.resume()
}
As before some JSON will appear (output as a String for convenience), and not much has changed here except that we're using a completion handler and accessing data rather than instantiating a String directly. However, note the use of XCPlaygroundPage.currentPage.needsIndefiniteExecution = true and XCPlaygroundPage.currentPage.finishExecution(). These are Playground specific and enable processes to keep running in order to make the network requests. Also note the use of dataTaskWithURL to create an NSURLSessionDataTask, which must be started with the resume method.

POST

In order to perform a POST task we need to use a NSURLRequest or in this case a NSMutableURLRequest:
import Foundation
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

if let url = NSURL(string: "http://httpbin.org/post") {
    
    let request = NSMutableURLRequest(URL: url)
    request.HTTPBody = "name=sketchyTech".dataUsingEncoding(NSUTF8StringEncoding)
    request.HTTPMethod = "POST"
    
    let session = NSURLSession.sharedSession()
    
    let dataTask = session.dataTaskWithRequest(request,completionHandler: {(data,_,_) in
        defer {
            XCPlaygroundPage.currentPage.finishExecution()
        }
        if let data = data, string = String(data: data, encoding: NSUTF8StringEncoding) {
            print(string)
        }
        }
    )
    
    dataTask.resume()
}
Here we set the HTTBody of the NSMutableURLRequest as required by a POST method and we specify the HTTPMethod as POST.  Then instead of dataTaskWithURL we use the very similar dataTaskWithURLRequest.

NSURLSession background sessions

Often you'll want the benefits of being able to access data using a background task. Here's some code to get that up and running:
import Foundation
import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

// delegate for receiving data
class SessionDelegate:NSObject, NSURLSessionDataDelegate {
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        session.configuration.identifier // check the identifier of the task
        if let string = String(data: data, encoding: NSUTF8StringEncoding) {
            string
        }
    }

}
// create a background session configuration with identifier
let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("myConfig")

if let url = NSURL(string: "https://itunes.apple.com/search?term=jack+johnson&limit=5") {
    let delegate = SessionDelegate()
    // create session by instantiating with configuration and delegate
    let session = NSURLSession(configuration: config, delegate: delegate, delegateQueue: nil)

    let dataTask = session.dataTaskWithURL(url)
    
    dataTask.resume()

}
Note the requirement of a delegate (completion handlers are not permitted in this approach) and a configuration instance. Without which the necessary NSURLSession cannot be instantiated.

Retrieving header information 

In all of the cases we can retrieve information such as the response status code and header information using the NSHTTPURLResponse class. For example where we are using a completion handler (and have access to the NSURLResponse):
if let response = response as? NSHTTPURLResponse {
    response.statusCode
    response.allHeaderFields
}
and where we have access to the dataTask in the delegate methods:
if let response = dataTask.response as? NSHTTPURLResponse {
    response.statusCode
    response.allHeaderFields
}
From the NSURLResponse itself, we can also obtain useful information, such as:
  • expectedContentLength
  • suggestedFilename
  • MIMEType
  • textEncodingName
  • URL

Conclusion

This post has sketched out a few of the basics and got things running in Swift Playgrounds. Apologies if it seems a bit messy, time was rather short. Next time I hope to move into using MAMP and to start uploading and downloading files.

Further reading

URLSession Programming Guide (Apple)

Appendix

This is a note for those attempting to employ these approaches beyond a Playground. Any attempt to employ NSURLSession in iOS 9 outside a Playground, within the Simulator or on a device, using a regular http address will result in a warning of the kind:
Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
But thankfully there's a way around this using the Info.plist file. And employing this approach your Info.plist will end up with a Dictionary entry like this:
And when working locally using MAMP you can disable ATPS entirely like so:

For more detail see Apple's docs.

Comments

  1. Found few examples on Internet and all are not working well, except this. Simple and straight forward answer to the question.

    ReplyDelete

Post a Comment