Parsing JSON APIs with SwiftyJSON

Parsing JSON using Swift is not a happy task. One of the first things I wanted to do in a Playground was call the topcoder API and start playing with the returned challenge data.

Unfortunately parsing JSON in data is almost akin to chewing glass right now (hopefully it will get better). You would think that since working with JSON is such a fundamental task nowadays, that it would be much easier, but no. I poked around for awhile and found David Owens' blog on JSON parsing but it was much more time than I wanted to invest in. I think JSON parsing should be simple and I think I finally found something that fits the bill.

I stumbled across the SwiftyJSON repo and it looks promising. It was much easier for me to grok and include in a Playground to test with.

So here a little snippet (thanks to Jameson Quave!) on how to call the topcoder API from Playground. Notice there's a spot in the code below (also available in this gist) where you simply paste in the SwiftyJSON code to use it.

import Foundation
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely()

/**
* Paste all the code from the following file
 - https://github.com/lingoer/SwiftyJSON/blob/master/SwiftyJSON/SwiftyJSON.swift
**/

let urlPath = "http://api.topcoder.com/v2/challenges?pageSize=2"
let url: NSURL = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
  
  if error != nil {
  // If there is an error in the web request, print it to the console
  println(error.localizedDescription)
  }
  
  var err: NSError?
  var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
  if err != nil {
  // If there is an error parsing JSON, print it to the console
  println("JSON Error (err!.localizedDescription)")
  }
  
  let json = JSONValue(jsonResult)
  let count: Int? = json["data"].array?.count
  println("found (count!) challenges")
  
  if let ct = count {
  for index in 0...ct-1 {
  // println(json["data"][index]["challengeName"].string!)
  if let name = json["data"][index]["challengeName"].string {
    println(name)
  }
  
  }
  }
})
task.resume()

The first thing we need to do is import the XCPlayground framework and add XCPSetExecutionShouldContinueIndefinitely(). This allows for asynchronous operations so that Playground can return the results from the API call after all top levels commands have executed and the process normally ends.

Then, of course, we need to paste the contents of the SwiftyJSON source into our Playground so we get all the JSON-parsing goodness.

The next few lines of code just setup our API call to retrieve our challenges JSON payload. We create the task on line 14 and then kick it off on line 42 with task.resume().

The meat of the Playground is the last parameter of dataTaskWithURL which is a closure that gets called upon completion of the request. Then we check for errors and parse the JSON result into a NSDictionary.

Here's where we start using SwiftyJSON, casting the JSON result as it's JSONValue. Now we can access it as a familiar associative array to find the nested values we need. Viola!