Updating NSURLSession Calls to Swift 3.0
September 23, 2016 - Swift 3.0

Now that Xcode 8 is really here, it’s about time we thought about updating some of our code to Swift 3.0. If you’ve started migrating to Swift 3.0 you know it can be a real pain. I’ve got plans to update a bunch of posts but let’s start with the simplest stuff: Making networking calls using NSURLSession and parsing the resulting JSON.

The first thing you need to know is that it’s not called NSURLSession anymore. In an effort to streamline our code (and make everything harder to google), tons of NS prefixes have been removed in Swift 3.0.

Programmer doing mobile dev
(image by #WOCinTech Chat)

This tutorial takes the Swift 2.2 code and shows how to update it to Swift 3.0 using Xcode 8.0.

GET Request

Here’s a simple Swift 2.2 example of making a GET request:


// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = NSURL(string: todoEndpoint) else {
  print("Error: cannot create URL")
  return
}
let urlRequest = NSURLRequest(URL: url)

// set up the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)

// make the request
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
  // do stuff with response, data & error here
  print(error)
  print(response)
})
task.resume()

If we try to run that code in Xcode 8 we’ll get a handful of errors:

Xcode trying to autocorrect errors

We could try letting Xcode correct those errors automatically using its suggestions. But sometimes it’s better to do things manually so we really understand what we’re changing. So let’s look at the official Swift 3.0 migration guide. It’d be a good idea to at least skim through that whole page now.

The part we need to deal with first is under SDK:

The ‘NS’ prefix from key Foundation types is getting removed in Swift 3, see SE-0086 - Drop NS Prefix in Swift Foundation.

Following that GitHub link sends us to a long list of places where the NS prefix has been removed. It also links to the new Swift Core Libraries documentation which includes new Swiftier versions of lots of classes like NSURL.

So our task is two-fold:

  1. Check if there’s a new Swift Core class and, if so, switch to it
  2. Otherwise check for any NS prefixes that need to be removed.

Fortunately, those two tasks pretty much come down to the same thing: delete NS at the start of any class names and see if the code compiles :)

So let’s try that with the GET request code (one chunk at a time). First we’ll remove all of the NS prefixes:


// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
  print("Error: cannot create URL")
  return
}
let urlRequest = URLRequest(URL: url)

// set up the session
let config = URLSessionConfiguration.defaultSessionConfiguration()
let session = URLSession(configuration: config)

// make the request
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
  // do stuff with response, data & error here
  print(error)
  print(response)
})
task.resume()

And now we have more errors. This happens a lot when migrating to Swift 3.0. Fortunately most of them aren’t too hard to figure out:

Xcode errors with code above

For the first error, Xcode is telling us exactly how to fix it: change URL: to url:


let urlRequest = URLRequest(url: url)

The second error seems more cryptic. It’s telling us that URLSessionConfiguration.defaultSessionConfiguration() doesn’t exist. Let’s try retyping the line to see what autocomplete suggests. When we get to URLSessionConfiguration. the autocomplete list includes default which looks like what we want:


let config = URLSessionConfiguration.default

Alternatively you could just use the shared session without setting up a URLSessionConfiguration:

let session = URLSession.shared

You should be noticing now that Swift 3.0 is a lot less wordy than previous versions. Often the fixes we need to make are shortening super verbose Objective-C style syntax.

That fixes those 2 errors but I’m getting another one now:


'dataTaskWithRequest(_:completionHandler:)' has been renamed to 'dataTask(with:completionHandler:)'

That’s another example of the less verbose Swift and the compiler being helpful. Let’s update that too:


let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
  // ...
})
task.resume()

Save & run to make sure that works for you. It should print out the response that looks something like:


nil
Optional( { URL: https://jsonplaceholder.typicode.com/todos/1 } { status code: 200, headers {
  ...

And that’s all it takes to update this networking call to Swift 3.0:

  1. Delete all of the NS-prefixes
  2. See what the compiler complains about
  3. Make the compiler happy (mostly deleting verbosity, relying on the docs and autocomplete when you need to)

Other conversions to Swift 3.0 can be a lot more complicated. We’ll get to some of those in future posts. It can be overwhelming when you’re converting a whole project, especially when every fix seems to pop up more errors. Focus on the simple fixes first. The more complex issues will be more obvious when they’re they only errors left.

Parsing JSON

I don’t often perform a networking call without parsing the resulting JSON. Here’s what the old code using NSJSONSerialization looked like:


let task = session.dataTaskWithRequest(urlRequest) {
  (data, response, error) in
  // check for any errors
  guard error == nil else {
    print("error calling GET on /todos/1")
    print(error)
    return
  }
  // make sure we got data
  guard let responseData = data else {
    print("Error: did not receive data")
    return
  }
  // parse the result as JSON, since that's what the API provides
  do {
    guard let todo = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as? [String: AnyObject] else {
      print("error trying to convert data to JSON")
      return
    }
    // now we have the todo, let's just print it to prove we can access it
    print("The todo is: " + todo.description)
    
    // the todo object is a dictionary
    // so we just access the title using the "title" key
    // so check for a title and print it if we have one
    guard let todoTitle = todo["title"] as? String else {
      print("Could not get todo title from JSON")
      return
    }
    print("The title is: " + todoTitle)
  } catch  {
    print("error trying to convert data to JSON")
    return
  }
}
task.resume()

To migrate to Swift 3.0 let’s do the same as above: remove the NS prefixes and see what happens (and we’ll update the dataTask function that we already figured out).

The only NS prefix is on NSJSONSerialization:


do {
  guard let todo = try JSONSerialization.JSONObjectWithData(responseData, options: []) as? [String: AnyObject] else {
    print("error trying to convert data to JSON")
    return
  }
  // ...
} catch  {
  print("error trying to convert data to JSON")
  return
}

And like before that triggers another error that’s just a renaming:

'JSONObjectWithData(_:options:)' has been renamed to 'jsonObject(with:options:)'

So we’ll update that call:


do {
  guard let todo = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
    print("error trying to convert data to JSON")
    return
  }
  // ...
} catch  {
  print("error trying to convert data to JSON")
  return
}

That should compile & run, printing out the description and title of the first todo:


The todo is: ["title": delectus aut autem, "userId": 1, "id": 1, "completed": 0]
The title is: delectus aut autem

And That’s All

That’s how you update an existing NSURLSession GET networking call (with NSJSONSerialization JSON parsing) to Swift 3.0. Over the next while I’ll be working through how to update more networking code to Swift 3.0, using URLSession and Alamofire. If there’s something specific you want to see, leave a comment or email me.

Here’s the final code: GitHub gist: URLSession Get Call in Swift 3.0

If you’d like more Swift tutorials on topics like this one, sign up below to get them sent directly to your inbox.

Want more Swift tutorials like this one?

Sign up to get the latest GrokSwift tutorials and information about GrokSwift books sent straight to your inbox

Other Posts You Might Like