When developing mobile apps, we often need to connect to back-end through REST APIs. URLSessionDataTask
and Alamofire are usually used for it. URLSession
It 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
Table of Contents
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 .httpMethod
set, 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 GET
Request, 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.