<– Back

Using Alamofire to send web requests and handling the JSON returned

HTTP Requests and REST

When you type an address into your browser, you’re basically asking it to open a channel to the server that responds to the URL you typed in. In this case, your browser is the client and the URL is the address that belonds to the server. Once the connection is established, your browser sends a GET request to load the webpage. GET requests are only one of many types of requests a client can send to the server. To maintain some sort of continuity across web services, we have RESTful architecture. There are two important properties of RESTful architecture:

Statelessness

Systems that follow this architecture are stateless. By that I mean, the server does not need to know what state is the client in and vice versa. The client and server can understand any message they receive without requiring previous messages.

Separation of Client and Server

The concerns of the client and the server and separate. The client does not need to manage data storage on the server, the server does not need to manage the user interface. Separation of the two allows them to evolve independently, thus improving scalability.

All APIs that adhere to the RESTful architecture accept a standard list of HTTP requests, often called verbs, like the GET request we just learned about.

  • GET: Retrieve a specific resource or a collection of resources.
  • POST: Create a new resource.
  • DELETE: Delete a resource.
  • PATCH: Send a set of instructions to modify an existing resource.
  • PUT: Replace a resource already residing on the server.

When building APIs, a few basic functionalities must be fulfilled. These functionalities as a group are called CRUD (Create, Read, Update, Delete). You can more about it here.

JSON

JSON is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate.
- json.org

Here’s an example of JSON data:

{
	"friends":[
	    {
	    	"firstName":"Tyler",
	    	"lastName":"Okonma"
	    }, 
	    {
	    	"firstName":"Jermaine",
	    	"lastName":"Cole"
	    },
	    {
	    	"firstName":"Kendrick",
	    	"lastName":"Lamar"
	    }
	]
}

It is basically an array of friends with each element being of a type Friend. We can define this structure as:

struct Friend {
	let firstName: String
	let lastName: String
}

Each Friend has two properties, a firstName and a lastName. We can access a Friend object’s firstName simply by typing friend.firstName.

Alamofire and URLSessions

Now, to make these requests through our app, we can either use URLSessions or Alamofire. Both of them are powerful enough to fulfil our needs. Alamofire is a third-party library while URLSessions is a library provided by Apple and does not require a separate library. It is always recommended to minimise the number of dependancies in your project to make sure updates to those dependancies does not break your code, but, Alamofire is a very popular library and is well documented. The chances of your code breaking because of Alamofire are next to none. We’re going to use Alamofire here, but you can perform the same functions using URLSessions too.

Follow these steps to install Alamofire into your project using Cocoapods:

  • Open Terminal and navigate to your project folder. Run

    pod init

This creates a Podfile in your project. A Podfile helps in managing all external libraries you are going to use inside the project.

  • Open the Podfile: open podfile
  • Now edit the Podfile to add Alamofire as a pod: pod 'Alamofire'
  • To install pods, close your project on Xcode and run: pod install

Open the .xcworkspace file instead of the usual .xcodeproj.

Now that we have Alamofire ready to go, we can start coding our request. To import Alamofire in a file, just type:

import Alamofire

Making a request using Alamofire

Let’s try to get meta data about a random XKCD comic. Here’s the url:

https://xkcd.com/614/info.0.json

Notice 614 is the id of the comic. If you change the number, the return value is for a different comic. You can also use this link to load the current XKCD comic into an imageView.

Here’s the JSON returned from the url:

{
  "month": "7",
  "num": 614,
  "link": "",
  "year": "2009",
  "news": "",
  "safe_title": "Woodpecker",
  "transcript": "[[A man with a beret and a woman are standing on a boardwalk, leaning on a handrail.]]\nMan: A woodpecker!\n<<Pop pop pop>>\nWoman: Yup.\n\n[[The woodpecker is banging its head against a tree.]]\nWoman: He hatched about this time last year.\n<<Pop pop pop pop>>\n\n[[The woman walks away.  The man is still standing at the handrail.]]\n\nMan: ... woodpecker?\nMan: It's your birthday!\n\nMan: Did you know?\n\nMan: Did... did nobody tell you?\n\n[[The man stands, looking.]]\n\n[[The man walks away.]]\n\n[[There is a tree.]]\n\n[[The man approaches the tree with a present in a box, tied up with ribbon.]]\n\n[[The man sets the present down at the base of the tree and looks up.]]\n\n[[The man walks away.]]\n\n[[The present is sitting at the bottom of the tree.]]\n\n[[The woodpecker looks down at the present.]]\n\n[[The woodpecker sits on the present.]]\n\n[[The woodpecker pulls on the ribbon tying the present closed.]]\n\n((full width panel))\n[[The woodpecker is flying, with an electric drill dangling from its feet, held by the cord.]]\n\n",
  "alt": "If you don't have an extension cord I can get that too.  Because we're friends!  Right?",
  "img": "https://imgs.xkcd.com/comics/woodpecker.png",
  "title": "Woodpecker",
  "day": "24"
}

To get this data in your app, use Alamofire to make a web request as follows:

let url = "https://xkcd.com/614/info.0.json"
    
let headers = [
    "Content-type": "application/json"
]
    
Alamofire.request(url, method: .get, parameters: nil, headers: headers)

Most requests require a parameter variable, which is used to send ids or any other resource to target a resource on the server. For example, if you want to get a resource with id = 4:

let parameters = [
	"id": 4
]

Instead of nil in the .request() function, use this variable.
Okay, so we made a request, but how do we see the response? Alamofire has a function called .responseJSON() which accepts a completion handler to handle the response of requests. Printing the response if the request was completed successfully is easy.

Alamofire.request(url, method: .get, parameters: nil, headers: headers).responseJSON { (response) in
    switch response.result {
    case .success:
            print(response)
            break
        case .failure(let error):
            print(error)
            break
    }
}

Here, we use a switch case to handle different cases. If it is a success, we print the response, otherwise we print the error.

Making a Codable structure to parse the JSON

So we got the data but how do we parse it? First we need to make a structure like the Friend structure we made.

struct Metadata: Codable {
    let alt: String?
    let day: Int?
    let img: String?
    let link: String?
    let month: Int?
    let news: String?
    let num: Int?
    let safe_title: String?
    let title: String?
    let transcript: String?
    let year: Int?
}

Here you will notice that our struct is of the type Codable. Codable is a type alias for the Encodable (a type that can encode itself to an external representation) and Decodable (a type that can decode itself to an external representation) protocols. By saying our struct is of type Codable, it will conform to both protocols, even though we only require Decodable here. Also something to note here is that all our properties are optional. By that, we want to tell our decoder that it is not necessary that we would receive a property called title or news. If we don’t declare them as optional, the app would crash if any propery is missing.

Using JSONDecoder to parse JSON

Now back to our response from the web request, we can start parsing the JSON returned.

Alamofire.request(url, method: .get, parameters: nil, headers: headers).responseJSON { (response) in
    switch response.result {
        case .success:
            if let data = response.data {
                do {
                    let response = try JSONDecoder().decode(Metadata.self, from: data)
                    print(response.title ?? "Whoops! No title found.")
                }
                catch let error {
                    print("error: ", error)
                }
            }
            break
        case .failure(let error):
            print(error)
            break
    }
}

Here, we’re making an object of type JSONDecoder which helps in decoding data into JSON objects. We then ask it to decode data of type Metadata. If it is done successfully, we can then use our new response variable of type Metadata to access all the data we received.

You can find the code here. Feel free to contact me anywhere anytime if you have any doubts.

iOS Swift JSON Web