Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4399

Working with JSON in Swift Tutorial

$
0
0

Learn how to parse JSON using Swift!

JavaScript Object Notation, or JSON for short, is a common way to transmit data to and from web services.

JSON is simple to use and it’s human-readable, which is why it’s so incredibly popular.

Consider the following JSON snippet:

[
  "person": {"name":"Dani","age":"24"},
  "person": {"name":"ray","age":"70"}
]

In Objective-C, parsing and deserializing JSON is fairly straightforward:

NSString *age = json[0][@"person"][@"age"];
NSLog(@"Dani's age is %@", age);

In Swift, parsing and deserializing JSON is a little more tedious due to Swift optionals:

if let item = json[0] {
  if let person = item["person"] {
    if let age = person["age"] {
      println(age)
    }
  }
}

In the code above, each object from the JSON must be checked before you use it via optional binding. This makes your code safe, but the more complicated your JSON gets, the more ugly your code gets.

In this tutorial, you’re going to learn an easier way to parse JSON in Swift – using a popular open source library called SwiftyJSON.

In particular, you’ll use SwiftyJSON to parse a JSON document containing the top 25 apps in the US App Store. You’ll see that parsing JSON in Swift can be just as easy as it is in Objective-C! :]

Note: This tutorial assumes that you have some basic knowledge of Swift. If you are new to Swift, check out our Swift Quick Start tutorial series.

Getting Started

Download the starter project for this tutorial, and build and run. You’ll see a blank view controller.

BlankViewControllerA user interface isn’t important for this tutorial, so you’ll be working with the console only. Get used to this blank white screen! :]

The starter project has a few files in it already, to keep the focus purely on parsing JSON in Swift. Take a look at the following files to get an overview of what’s included:

  • TopApps.json: Contains the JSON string to be parsed.
  • AppModel: A plain old Swift object representing an app. Your goal is to parse the JSON into a collection of these objects.
  • DataManager: Manages the data retrieval whether local or from the network. You’ll use the methods in this file to load some JSON later in this tutorial.
  • ViewController: An empty view controller. You’ll add code here to request data from DataManager.swift and parse it in several different ways.

Once you feel like you have a good understanding of what’s currently in the starter project, read on!

Parsing JSON the Native Swift Way

Note: If you already know about the pain of parsing JSON the native way and want to jump straight to SwiftyJSON, skip to the next section.

First, you’ll start by parsing JSON the native Swift way – i.e. without using any external libraries. This will help you understand the benefit of using a library like SwiftyJSON.

Let’s parse the provided JSON file to get the name of the #1 app on the App Store.

Open ViewController.swift and add the following code to the end of viewDidLoad():

DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in
  // Get the number 1 app using optional binding and NSJSONSerialization
  //1
  var parseError: NSError?
  let parsedObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data,
    options: NSJSONReadingOptions.AllowFragments,
    error:&parseError)
 
  //2
  if let topApps = parsedObject as? NSDictionary {
    if let feed = topApps["feed"] as? NSDictionary {
      if let apps = feed["entry"] as? NSArray {
        if let firstApp = apps[0] as? NSDictionary {
          if let imname = firstApp["im:name"] as? NSDictionary {
            if let appName = imname["label"] as? NSString {
              //3
              println("Optional Binding: \(appName)")
            }
          }
        }
      }
    }
  }
}

Are you rolling your eyes already at that list of cascading closing braces? :] Here’s what’s going on above:

  1. First you deserialize the data using NSJSONSerialization.
  2. You need to check each and every subscript value in the JSON object to make sure that it is not nil. Once you’re sure it has a valid value, search for the next object. Once you’ve made your way through all of the subscripts, you’ll get the appName value to display.


    Note that if any element in the JSON is unexpected, you bail on the whole mess and the app name never gets printed. This is desirable in this case.

  3. The last step simply prints the value of appName to the debugger.

Build and run your project; you should see the following result in your debugger console:

Optional Binding: Clash of Clans

Yes — “Clash of Clans” is the #1 app in the JSON file.

It took a lot of code just to retrieve the name of the first app — it’s time to see how SwiftyJSON stacks up.

Integrating SwiftyJSON in Your Project

It’s fairly straightforward to integrate SwiftyJSON into your project.

Simply go to the SwiftyJSON Github page and download the library to a convenient location.

Then drag SwiftyJSON\SwiftyJSON.swift into your project navigator in Xcode. Make sure to select Copy items if needed, make sure that your TopApps target is selected, and click Finish.

That’s it! Now that you have SwiftyJSON integrated into your project, you can start parsing JSON without the optional binding headache!

Parsing JSON the SwiftyJSON Way

SwiftyJSON lets you eliminate all of those nil checks so you can get the name of the #1 app in a single if statement.

Replace viewDidLoad() with the following:

override func viewDidLoad() {
  super.viewDidLoad()
 
  DataManager.getTopAppsDataFromFileWithSuccess { (data) -> Void in
    // Get #1 app name using SwiftyJSON
    let json = JSON(data: data)
    if let appName = json["feed"]["entry"][0]["im:name"]["label"].stringValue {
      println("SwiftyJSON: \(appName)")
    }
  }
}

Seriously — that’s all the code you need.

First you create a JSON constant via the JSON() init method and your data object. SwiftyJSON then converts this data into a JSON object using NSJSONSerialization.

The bonus here is that SwiftyJSON takes care of all the optional validation that you previously had to code by hand. You only need to know which keys and indices you want to access and you can leave the rest to SwiftyJSON.

In the above case, you want to retrieve a string value so you pass .stringValue at the end of the parsing process. If you wanted an array, you would pass .arrayValue. Other return types use a similar construct.

Build and run your app; you’ll see that you successfully get the app name again, but this time with a lot less code:

SwiftyJSON: Clash of Clans

That takes care of parsing local data — but how would you take care of parsing data from a remote source?

Retrieving Remote JSON

It’s time to make this project a bit more realistic. Normally, you’d be retrieving data remotely, not from a local file. You can easily grab the App Store ratings using an online request.

Go to DataManager.swift and add the following method:

class func getTopAppsDataFromItunesWithSuccess(success: ((iTunesData: NSData!) -> Void)) {
  //1
  loadDataFromURL(NSURL(string: TopAppURL)!, completion:{(data, error) -> Void in
      //2
      if let urlData = data {
        //3
        success(iTunesData: urlData)
      }
  })
}

The above code looks pretty familiar; but instead of retrieving a local file you’re using NSURLSession to pull the data from iTunes. Here’s what’s happening in detail:

  1. First you call loadDataFromURL; this takes the URL and a completion closure that passes in an NSData object.
  2. Next you make sure the data exists using optional binding.
  3. Finally, you pass the data to the success closure as you did before.

Open ViewController.swift and add the following line to the end of viewDidLoad():

// Get the #1 app name from iTunes and SwiftyJSON
DataManager.getTopAppsDataFromItunesWithSuccess { (iTunesData) -> Void in
  let json = JSON(data: iTunesData)
  if let appName = json["feed"]["entry"][0]["im:name"]["label"].stringValue {
    println("NSURLSession: \(appName)")
  }
  // More soon...
}

The above code is almost identical to what you did in the first section — only now you’re retrieving real data from iTunes.

Build and run your project; you’ll see that the data parsing still arrives at the same conclusion of the #1 app:

SwiftyJSON: Clash of Clans
NSURLSession: Clash of Clans

The above value may be different for you, as the top apps in the App Store change constantly.

Usually people aren’t just interested in the top app in the App Store — they want to see a list of all top apps. That means you’ll need to retrieve an array of app store ratings instead.

Parsing JSON for Arrays

Add the following code to ViewController.swift just after the “More soon” comment:

//1
if let appArray = json["feed"]["entry"].arrayValue {
  //2
  var apps = [AppModel]()
 
  //3
  for appDict in appArray {
    var appName: String? = appDict["im:name"]["label"].stringValue
    var appURL: String? = appDict["im:image"][0]["label"].stringValue
 
    var app = AppModel(name: appName, appStoreURL: appURL)
    apps.append(app)
  }
 
  //4
  println(apps)
}

The code above iterates over all apps in the JSON response and creates a model object of AppModel as follows:

  1. First you retrieve the list of apps with SwiftyJSON.
  2. Next you create a mutable array to hold the objects to be created.
  3. You then loop through all the items and create a new instance of AppModel from the JSON data.
  4. Finally, you print the new objects out to the debugger.

Build and run your app; you’ll see the previous three results from your JSON parsing attempts as well as a list of the top 25 apps in the iTunes store as shown below:

TopApps4

In real-life applications, you could use all the code you wrote above and implement a UICollectionView or a UITableView to display the results instead.

And that’s it! That’s all there is to using JSON in Swift. The rest of this tutorial is optional, if you want to learn more about how SwiftyJSON works under the hood.

Peeking Under the Hood at SwiftyJSON

You can definitely see that SwiftyJSON works really well to collapse your code into one or two lines — but how does it work under the hood?

When you call JSON() you pass an instance of an optional NSData or even AnyObject. JSONValue() is the initializer for an enumeration in SwiftyJSON:

public enum JSON {
    case ScalarNumber(NSNumber)
    case ScalarString(String)
    case Sequence(Array<JSON>)
    case Mapping(Dictionary<String, JSON>)
    case Null(NSError?)
    // ...
}

Remember that to retrieve different different values from the returned JSON object you simply access different keys and indices. Here’s the code you used to get the name of the #1 app on the App Store:

 json["feed"]["entry"][0]["im:name"]["label"]

This works under the hood through Swift subscripting:

extension JSON {    
    // ...    
    subscript(key: String) -> JSON {
        get {
            switch self {
            case .Mapping(let dictionary) where dictionary[key] != nil:
                return dictionary[key]!
            default:
                return .Null(NSError(domain: SwiftyJSONErrorDomain, code: 0, userInfo: nil))
            }
        }
    }
}

Note the result of the subscript operator is another JSON object. It’s up to you to decide what the expected type is that you wish to retrieve. In this case, you expect this to return the app’s name as a String, so you add a .stringValue to the end of the JSON object.

extension JSON {
 
    var stringValue: String? {
        get {
            switch self {
            case .ScalarString(let string):
                return string
            case .ScalarNumber(let number):
                return number.stringValue
            default:
                return nil
            }
        }
    }
 
    // ...
}

The string() method is one of a set of methods named after the various types you can retrieve from a JSON object; each function returns an optional of its namesake type.

var stringValue: String?
var numberValue: NSNumber?
var URLValue: NSURL?
var charValue: Int8?
var unsignedCharValue: UInt8?
var shortValue: Int16?
var unsignedShortValue: UInt16?
var longValue: Int?
var unsignedLongValue: UInt?
var longLongValue: Int64?
var unsignedLongLongValue: UInt64?
var floatValue: Float?
var doubleValue: Double?
var integerValue: Int?
var unsignedIntegerValue: Int?

Notice that all of these values are optionals which avoids crashes if the value is nil. This is how SwiftyJSON handles optional binding.

You haven’t touched on error handling in SwiftyJSON yet, but it’s equally simple.Here’s how to handle an error condition in SwiftyJSON:

let json = JSON(dataFromNetworking)["some_key"]["some_wrong_key"]["wrong_name"]
if json{
  // Handle success
} else {
  println(json)
  //> JSON Keypath Error: Incorrect Keypath "some_wrong_key/wrong_name"
  //It always tells you where your key went wrong
  switch json{
  case .JInvalid(let error):
    //An NSError containing detailed error information 
  }
}

I highly recommend looking through SwiftyJSON.swift to see how it works. It’s a great example of how to combine a number of useful Swift techniques for a handy result!

Where to Go From Here?

Here is the final example project from the above tutorial.

You can definitely use the above project as the starter code for your next app; simply replace the URL for retrieving remote data with your own URL, handle the individual keys and indices of your new JSON response, and you’ll have a new project that parses something else like the results of football matches or any other piece of data that can be downloaded over the network as JSON.

Development on SwiftyJSON will continue here on Github, so keep an eye out for the latest updates.

Swift is still evolving as well, so you’ll want to keep up to date on Apple’s documentation for any future updates for the language.

I hope you enjoyed this tutorial; don’t forget to check out all the other Swift tutorials on the website. If you have any questions or comments, feel free to join the discussion below!

Working with JSON in Swift Tutorial is a post from: Ray Wenderlich

The post Working with JSON in Swift Tutorial appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4399

Trending Articles