Working with JSON in Swift

November 21, 2018
Written by
Sam Agnew
Twilion

Swift JSON

Often developers need to deal with data in various different formats and JSON, short for JavaScript Object Notation, is one of the most popular choices. This is the syntax that the JavaScript language uses to denote objects.

For this post, we are going to use the following modified JSON data from NASA's Astronomy Picture of the Day API to cover some basic examples of scenarios where you'd need to parse JSON. You will need to be running Swift 4.0 or greater. The following JSON is the example that we are going to be working with:

{
    "copyright": "Yin Hao",
    "date": "2018-10-30",
    "explanation": "Meteors have been shooting out from the constellation of Orion. This was expected, as October is the time of year for the Orionids Meteor Shower. Pictured here, over two dozen meteors were caught in successively added exposures last October over Wulan Hada volcano in Inner Mongolia, China. The featured image shows multiple meteor streaks that can all be connected to a single small region on the sky called the radiant, here visible just above and to the left of the belt of Orion, The Orionids meteors started as sand sized bits expelled from Comet Halley during one of its trips to the inner Solar System. Comet Halley is actually responsible for two known meteor showers, the other known as the Eta Aquarids and visible every May. An Orionids image featured on APOD one year ago today from the same location shows the same car. Next month, the Leonids Meteor Shower from Comet Tempel-Tuttle should also result in some bright meteor streaks. Follow APOD on: Facebook, Instagram, Reddit, or Twitter",
    "hdurl": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_2324.jpg",
    "media_type": "image",
    "service_version": "v1",
    "title": "Orionids Meteors over Inner Mongolia",
    "url": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_960.jpg"
}

Let's gear up to parse some JSON!

Codable in Swift 4

For a long time, working with JSON in Swift was thought of as a relatively painful and annoying experience. But with the release of Swift 4 and the Codable protocol, which combines the functionality of Encodable and Decodable, we now have an out-of-the-box solution and don't have to use external JSON libraries.

For this example, we'll create a Codable struct that represents the data we would get back from the Astronomy Picture of the Day API and try to decode the JSON data using this struct. Create a file called test.swift and paste the following code into it:

import Foundation

// A multi-line string literal representing the JSON we want to work with.
let jsonString = """
                 {
                     "copyright": "Yin Hao",
                     "date": "2018-10-30",
                     "explanation": "Meteors have been shooting out from the constellation of Orion. This was expected, as October is the time of year for the Orionids Meteor Shower. Pictured here, over two dozen meteors were caught in successively added exposures last October over Wulan Hada volcano in Inner Mongolia, China. The featured image shows multiple meteor streaks that can all be connected to a single small region on the sky called the radiant, here visible just above and to the left of the belt of Orion, The Orionids meteors started as sand sized bits expelled from Comet Halley during one of its trips to the inner Solar System. Comet Halley is actually responsible for two known meteor showers, the other known as the Eta Aquarids and visible every May. An Orionids image featured on APOD one year ago today from the same location shows the same car. Next month, the Leonids Meteor Shower from Comet Tempel-Tuttle should also result in some bright meteor streaks. Follow APOD on: Facebook, Instagram, Reddit, or Twitter",
                     "hdurl": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_2324.jpg",
                     "media_type": "image",
                     "service_version": "v1",
                     "title": "Orionids Meteors over Inner Mongolia",
                     "url": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_960.jpg"
                 }
                 """

// The struct we are going to use to encode/decode the JSON data above.
struct APOD: Codable {
  let copyright: String
  let date: String
  let explanation: String
  let hdurl: String
  let media_type: String
  let service_version: String
  let title: String
  let url: String
}


// Convert the jsonString to .utf8 encoded data.
let jsonData = jsonString.data(using: .utf8)!

// Create a JSON Decoder object.
let decoder = JSONDecoder()

// Decode the JSON data using the APOD struct we created that follows this data's structure.
let apod = try! decoder.decode(APOD.self, from: jsonData)

print(apod.explanation)

Notice that this code is using a struct that contains all of the keys from our JSON example. We are also using a try! statement just to show how this example works, but in any production-level code, you will want to catch whatever possible errors could arise.

In your terminal, run your code with the following command from the directory test.swift is in:

swift test.swift

You should see the explanation of the Astronomy Picture of the Day print to the terminal.

One of the upsides about using a Codable struct is that you don't have to install any third-party libraries, allowing you to require minimal dependencies.

SwiftyJSON

Before Swift 4, SwiftyJSON was one of the most popular libraries for working with JSON. The developers of SwiftyJSON argue that since JSON data is implicit about types by nature, that it is very well-suited to dealing with this data in general. SwiftyJSON takes care of optional-wrapping by default and decodes your JSON data into a Dictionary, behaving very similarly to JSON libraries in other popular programming languages such as request in Python.

Because SwiftyJSON is a third-party library, we will install it using Swift Package Manager. Open your terminal, navigate to where you want this code to live, and run the following command to initiate a Swift project to use Swift Package Manager:

swift package init --type executable

This will generate a Swift project with a directory structure for a command-line executable. To add SwiftyJSON to this project, open up the Package.swift file that was generated and add the following line to the dependencies array:

.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),

Now add the string "SwiftyJSON" to the dependencies array in the targets section to make sure that your code will be able to use this library.

With the dependencies taken care of, open the Sources/project/main.swift file (where "project" in this case will be replaced by whatever your project's name is, defaulting to the name of the directory it is contained in) and replace the code in it, which just has a "Hello World" print statement, with the following:

import SwiftyJSON

// A multi-line string literal representing the JSON we want to work with.
let jsonString = """
                 {
                     "copyright": "Yin Hao",
                     "date": "2018-10-30",
                     "explanation": "Meteors have been shooting out from the constellation of Orion. This was expected, as October is the time of year for the Orionids Meteor Shower. Pictured here, over two dozen meteors were caught in successively added exposures last October over Wulan Hada volcano in Inner Mongolia, China. The featured image shows multiple meteor streaks that can all be connected to a single small region on the sky called the radiant, here visible just above and to the left of the belt of Orion, The Orionids meteors started as sand sized bits expelled from Comet Halley during one of its trips to the inner Solar System. Comet Halley is actually responsible for two known meteor showers, the other known as the Eta Aquarids and visible every May. An Orionids image featured on APOD one year ago today from the same location shows the same car. Next month, the Leonids Meteor Shower from Comet Tempel-Tuttle should also result in some bright meteor streaks. Follow APOD on: Facebook, Instagram, Reddit, or Twitter",
                     "hdurl": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_2324.jpg",
                     "media_type": "image",
                     "service_version": "v1",
                     "title": "Orionids Meteors over Inner Mongolia",
                     "url": "https://apod.nasa.gov/apod/image/1810/Orionids_Hao_960.jpg"
                 }
                 """

if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) {
  let json = try! JSON(data: dataFromString)
  print(json["explanation"])
}

This code uses SwiftyJSON to parse the JSON in our jsonString and decodes it into a dictionary. After this, we're printing the explanation of the Astronomy Picture of the Day, just as we did in the last example.

Run this with the following command in the Sources/project directory to see ______:

swift run

Using Codable structs is great when the data you're working with has a consistent structure that you know about ahead of time, but SwiftyJSON works well in more general cases where you aren't as sure about the structure of the data you're dealing with.

Alamofire

Dealing with hard-coded JSON strings is fine and all, but often in the real world there is more context around why you have to deal with JSON data. One of the most common scenarios that requires decoding JSON would be when making HTTP requests to third-party REST APIs.

The Alamofire library is a popular tool for making HTTP requests, and it has a built-in method for parsing JSON when you receive an HTTP response. It's great to have a quick solution built into your networking library.

As we did with SwiftyJSON, add Alamofire to the dependencies in Package.swift, and don't forget to add "Alamofire" to your targets as well:

.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0"),

In this example, we are actually going to make an HTTP request to the Astronomy Picture of the Day API rather than using the hard-coded .json string from the other examples.

Replace the code in your main.swift with the following:

import Foundation
import Alamofire

Alamofire.request("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY").responseJSON { response in
  print("Result: \(response.result)")                         // response serialization result

  if let json = response.result.value {
    print("JSON: \(json)") // serialized json response
  }
}

RunLoop.main.run()

This code makes an HTTP GET request to NASA's API, then parses and prints out the JSON data returned from it. From here, you can use the data however you want.

Run your code with the following command:

swift run

The output should look something like this:

Conclusion

There are different solutions to working with JSON in Swift, and you may want to use these in different contexts, or even combine them. In this post I've shown you the basics of just a few of the more popular examples.

You can also check out this post to learn how to make HTTP requests in Swift.

Feel free to reach out for any questions or to show off what you build: