How to download and read JSON

In this mini tutorial we will show you how to download and read JSON from a server and displaying the output in Xcode.

There are a few JSON parsing libraries for Swift floating around on the internet and while they may be useful in some situations, if you only need to parse a simple JSON file, it may be overkill. In this mini-tutorial, we will make use of the built in NSJSONSerialization to read our JSON file.

In our example JSON, we have a list of Berlin subway stations, each with a stationName attribute and a buildYear attribute.

{
  "stations" : [
    {
      "stationName" : "Wittenbergplatz",
      "buildYear" : "1912"
    },
    {
      "stationName" : "Spichernstrasse",
      "buildYear" : "1959"
    },
    {
      "stationName" : "Weberwiese",
      "buildYear" : "1930"
    }
  ]
}

This file can be downloaded and viewed at the following URL:
http://www.learnswiftonline.com/Samples/subway.json
Before we start
We need to add the NSAppTransportSecurity key inside the Info.plist before we begin this project. This will allow the app to communicate with our servers to get the JSON file. This is not unique to this tutorial. Familiarize yourself with this step, you will be using it in the future.

Open the Info.plist, then right click underneath the last item and select”Show raw keys and values”.  This will show the actual key values for each row.

Now right click underneath the last item and select “Add row”. Type in NSAppTransportSecurity and press enter to register it. Once it has been added to your list, right click the item and select “Add row”. Then type in “NSAllowsArbitraryLoads” and set the value to YES.  The following screenshot illustrates what needs to be done.

Swift-tutorial

 

Downloading the file
In this mini-tutorial, we are using NSURLSession to download the JSON file from our server.

NSURLSession
First we create a NSURLSession instance and then a task to go along with it. At the end of the function, we place the task.resume, which starts the task. For the purpose of our tutorial, we will be adding this code to the viewDidLoad() function.

override func viewDidLoad() {

        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(urlRequest) {
            (data, response, error) -> Void in
            
            let httpResponse = response as! NSHTTPURLResponse
            let statusCode = httpResponse.statusCode
            
            if (statusCode == 200) {
                print("Everyone is fine, file downloaded successfully.")
            }
        }
        
        task.resume()
        
    }

Running this code should result in a success message outputted to your console. If not, check what the status code is to narrow down the problem.

Checking the response
In the above example, we grab the status code from the httpResponse to analyze it. We can then check to see if the status code was 200, which indicates a successful transfer. List of HTTP status codes.

Reading the JSON

Now that we have downloaded our JSON, we need to sort through it.

do/catch
You may notice that we are surrounding our request with do/catch. This is because it’s possible JSONObjectWithData: may throw an error. This could be due to the file being unavailable or the source JSON is not in the correct format (among many other issues). When the request fails, it is caught and prints a message to the console.

First we sort through the stations key and cast the data into an array of dictionaries. The array of dictionaries is assigned to constant (let) stationsArray. If this is successful, the if block is executed and the array of dictionaries is created.

Now that we have our stationsArray, we can loop through it using a simple for loop and read the stationName key and buildYear key of each object.

We read the value for stationName by creating a constant called name and then we read the stationName attribute from the dictionary. If it exists, the if block will execute. The same thing happens with the buildYear attribute from the dictionary, and when successful, prints the station name and build year to the console.

if (statusCode == 200) {
                    
     do{
                        
          let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
                    
          if let stations = json["stations"] as? [[String: AnyObject]] {
                        
             for station in stations {
                            
                 if let name = station["stationName"] as? String {
                                
                    if let year = station["buildYear"] as? String {             
                    print(name,year)
                    }
                                
                 }
           }
                        
     }

     }catch {
     print("Error with Json: \(error)")
     }

}

In your application, you’re probably going to want to do something specific with your JSON as opposed to simply printing it to the console. Once you have your array of dictionaries (stations), you can sort it, load it into a UITableView or do whatever else you would like.

Combining both the downloading of the JSON and the parsing, we get this final result:


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(urlRequest) {
            (data, response, error) -> Void in
            
            let httpResponse = response as! NSHTTPURLResponse
            let statusCode = httpResponse.statusCode
            
            if (statusCode == 200) {
                print("Everyone is fine, file downloaded successfully.")
                
                do{
                    
                    let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
                    
                    if let stations = json["stations"] as? [[String: AnyObject]] {
                        
                        for station in stations {
                            
                            if let name = station["stationName"] as? String {
                                
                                if let year = station["buildYear"] as? String {
                                    print(name,year)
                                }
                                
                            }
                        }
                        
                    }
                    
                }catch {
                    print("Error with Json: \(error)")
                }
                
            }
        }
        
        task.resume()
    }

8 comments

Mikhael February 18, 2016 at 12:32 pm

Thank you very much for this!

Reply
Mikhael February 18, 2016 at 2:22 pm

One question though: I would like to be able to change the let requestURL: NSURL = NSURL(string: “http://www.learnswiftonline.com/Samples/subway.json”)!
based on a bool value of switch or I would like to make it a parameter of a function.
However, when I try to do that an Xcode takes focus out from the simulator, showing this in a console area.
http://imgur.com/D60sFZo
at that time, the app gets frozen on a touched Button.
My View looks like this:
http://imgur.com/CjYenN5

What should I do? Please help me to understand what is happening and fix it.

The actual code can be seen at http://pastebin.com/qA64UcCM

Reply
gary March 31, 2016 at 2:01 am

The code snippet didn’t work for me, apparently due to some problem with the dataTaskWithRequest method. The task properties were:

task.countOfBytesReceived 0
task.currentReques Optional( { URL: http://www.learnswiftonline.com/Samples/subway.json })
task.response nil
task.error nil
task.taskDescription nil
task.taskIdentifier 1

These are probably the values that “task” was initialized to. It looks like the “dataTaskWithRequest” method did not run.

Reply
Ayushman April 11, 2016 at 5:25 am

if there was no “stations” object then how could we parse other value?

Reply
louis May 9, 2016 at 3:31 pm

what if i would like to only get the second station name and the second build year? how can i get that info?

Reply
lso_admin May 19, 2016 at 10:52 am

In this example, if you want to only save the second item, you would probably create an integer before the station loop that would keep track of which item you were on and once it “hit” the second one, save that to a previously created variable.

Reply
Jay May 11, 2016 at 3:34 pm

Doesn’t work. Did something Change?

Reply
Ryan May 19, 2016 at 11:25 am

I’ve added NSAppTransportSecurity information and changed a bit of the code. Everything should work fine now.

Reply

Leave a Comment