Requests to REST APIs with URLSessionDataTask & Alamofire

Photo by Vladislav Muslakov on Unsplash
Photo by Vladislav Muslakov on Unsplash
URLSessionDataTask and Alamofire are usually used for it. URLSessionIt is built in iOS SDK, while Alamofire is a very popular third-party framework.

When developing mobile apps, we often need to connect to back-end through REST APIs. URLSessionDataTask and Alamofire are usually used for it. URLSessionIt is built in iOS SDK, while Alamofire is a very popular third-party framework. This article will explain how to use them to connect to REST APIs and get JSON data.

The complete code can be found in

URLSessionDataTask

URLSession is a built-in class in iOS SDK, and its advantage is that there is no need to install other third-party frameworks.

GET Request

When use URLSession, we must first prepare a URLRequest. Set URLRequest.httpMethod to GET, and it is case insensitive. Then, append the data to be sent to the Query String after ?.

guard let url = URL(string: "https://postman-echo.com/get?foo1=bar1&foo2=bar2") else {
    print("Error: can not create URL")
    return
}

var request = URLRequest(url: url)
request.httpMethod = "get"

Next, call URLSession.dataTask(with:) to create URLSessionDataTask, and pass URLRequest and a response handler as parameters. Finally, remember to call URLSessionDataTask.resume()to launch the task.

When it calls the response handler, it will give three parameters. If the third parameter error is not nil, it indicates the task has failed.

The first parameter data is the body of the REST API response. it is type of Data, but the data content is a JSON string. Convert the data to Dictionary using JSONSerialization.jsonObject(with:).

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let error = error {
        print(error)
        return
    }

    guard let data = data else {
        print("Did not receive data")
        return
    }

    do {
        let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
        let args = json["args"] as! [String: String]
        print(args["foo1"]!)
        print(args["foo2"]!)
    } catch {
        print("Error: can not convert data to JSON")
        return
    }
}
task.resume()

POST Request

Like GET request, we must first create an URLRequest, and set POST to .httpMethodset, and set the data to .httpBody.

Because the data to be sent a JSON string, so we need to set Content-Type to application/json in the header in URLRequest. So that, the back-end will know the HTTP body contains a JSON string. We put the data into a Dictionary, and convert it to Data by JSONSerialization.data(withJSONObject:), set the Data to .httpBody.

guard let url = URL(string: "https://postman-echo.com/post") else {
    print("Error: can not create URL")
    return
}

var request = URLRequest(url: url)
request.httpMethod = "post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let data = [
    "foo1": "bar1",
    "foo2": "bar2",
]

do {
    request.httpBody = try JSONSerialization.data(withJSONObject: data)
} catch {
    print(error)
    return
}

Like GETRequest, call URLSession.dataTask(with:) to transmit data.

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let error = error {
        print(error)
        return
    }

    guard let data = data else {
        print("Did not receive data")
        return
    }

    do {
        let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
        let args = json["data"] as! [String: String]
        print(args["foo1"]!)
        print(args["foo2"]!)
    } catch {
        print("Error: can not convert data to JSON")
        return
    }
}
task.resume()

Alamofire

URLSessionDataTask is not very easy to use. For example, you need to manually append the data of GET requests to the Query String. For POST requests, it’s even worse. you need to convert your data to a JSON string, and then convert the JSON string to Data. In addition, the conversion of the data in the response handler is all done by yourself.

Alamofire is mainly to help us deal with the conversion of these data. Let’s see how it works.

First, install Alamofire framework. If you are using CocoaPods, put this line in your Podfile.

pod 'Alamofire', '~>5.2'

If you are not familiar with CocoaPods operation, you can refer to this article.

GET Request

Put the data we want to sent to a Dictionary. Call AF.request(), then pass a response handler in .responseJSON().

.responseJSON()will convert the received JSON string to a Dictionary, so we can directly use it.

import Alamofire

let parameters = [
    "foo1": "bar1",
    "foo2": "bar2",
]
AF.request("https://postman-echo.com/get", method: .get, parameters: parameters)
    .responseJSON { response in
        switch response.result {
        case .success(_):
            let json = response.value as! [String: Any]
            let args = json["args"]! as! [String: String]
            print(args["foo1"]!)
            print(args["foo2"]!)

        case .failure(let error):
            print(error)
        }
    }

POST Request

Sending POST requests is almost the same as sending GET requests. We also need to set Content-Type to application/json in order to tell the backend the data is a JSON string.

In addition, we need to pass JSONEncoding.default to tell Alamofire to convert parameters to a JSON string.

let headers: HTTPHeaders = [
    "Content-Type": "application/json"
]
let parameters = [
    "foo1": "bar1",
    "foo2": "bar2",
]
AF.request("https://postman-echo.com/post",
           method: .post,
           parameters: parameters,
           encoding: JSONEncoding.default,
           headers: headers)
    .responseJSON { response in
        switch response.result {
        case .success(_):
            let json = response.value as! [String: Any]
            let args = json["data"]! as! [String: String]
            print(args["foo1"]!)
            print(args["foo2"]!)

        case .failure(let error):
            print(error)
        }
    }

Advanced Use

The way to use Alamofire we just introduced has a problem that is we use Dictionary to contain the data we want to send. It’s not convention and not object-oriented. Let’s modify the code.

Declared struct PostRequest to represent the data to be sent, and implement Encodable. In addition, we would also like to add a computed property – var dictionary: [String: Any] in Encodable. It will convert an object to a Dictionary.

extension Encodable {
    var dictionary: [String: Any] {
        (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any] ?? [:]
    }
}

struct PostRequest: Encodable {
    let foo1: String
    let foo2: String
}

Declare struct PostResponse and struct PostResponseData to represent response data, and implement Decodable.

struct PostResponse: Decodable {
    let data: PostResponseData
}

struct PostResponseData: Decodable {
    let foo1: String
    let foo2: String
}

Then use .responseDecoablde(of:) to handle response data, and Alamofire will automatically convert response data into a PostResponse object.

let headers: HTTPHeaders = [
    "Content-Type": "application/json"
]
let parameters = PostRequest(foo1: "bar1", foo2: "bar2")
AF.request("https://postman-echo.com/post",
           method: .post,
           parameters: parameters.dictionary,
           encoding: JSONEncoding.default,
           headers: headers)
    .responseDecodable(of: PostResponse.self) { response in
        switch response.result {
        case .success(let value):
            print(value.data.foo1)
            print(value.data.foo2)

        case .failure(let error):
            print(error)
        }
    }

Is the modified code more object-oriented?

Conclusion

REST API is the most popular way to communicate between front and back ends. When developing iOS, we have to deal with a large number of REST APIs. Compared URLSessionDataTask to Alamofire, Alamofire is more convenient to use. Not only does it help us convert returned data, but also the unity GET and POST request methods.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like
Photo by Alex Alvarez on Unsplash
Read More

Dispatch Queue Tutorial

Grand Central Dispatch (GCD) provides efficient concurrent processing so that we don’t need to directly manage multiple threads. Its dispatch queues can execute tasks serially or concurrently.
Read More