Making GET Requests and Handling Responses

Welcome to the second lesson in our journey of interacting with APIs in Swift. Previously, we laid a strong foundation by understanding RESTful APIs and how HTTP requests enable interactions with them. This lesson introduces URLSession, a powerful native framework in Swift for making HTTP requests and handling responses directly in our code. By using Swift's URLSession, we can efficiently automate the process of interacting with APIs, thus enhancing our development workflow in iOS applications.

Setting Up the Environment

To make HTTP requests in Swift, we use URLSession, which is a built-in API provided by Apple's Foundation framework. This means you don’t need to install any additional libraries to start working with it. Simply import the Foundation framework at the top of your Swift file:

Swift
1import Foundation 2import FoundationNetworking

With this setup, you'll be ready to use URLSession to automate requests and handle API integration efficiently.

Defining the Base URL

When interacting with an API in Swift, it's useful to define a base URL for the endpoint you are working with. This makes the code more modular and easier to maintain, allowing you to update the base URL for your API requests in a single place.

Swift
1// Base URL for the API 2let baseURL = "http://localhost:8000"

By defining the baseURL as a constant, your code becomes cleaner and more adaptable for future changes.

Performing a Basic GET Request

Let's dive into the process of fetching data from an API using Swift's URLSession. Our goal is to retrieve a list of to-do items from the /todos endpoint using the GET method.

Swift
1// Create URL 2if let url = URL(string: "\(baseURL)/todos") { 3 let dispatchGroup = DispatchGroup() 4 dispatchGroup.enter() 5 6 // Create a URLSession data task 7 let task = URLSession.shared.dataTask(with: url) { data, response, error in 8 defer { dispatchGroup.leave() } 9 10 // Check if an error occurred 11 if let error = error { 12 print("Error occurred: \(error.localizedDescription)") 13 return 14 } 15 16 // Ensure there is data returned from this HTTP response 17 guard let data = data else { 18 print("No data received") 19 return 20 } 21 22 // Print raw response 23 if let responseString = String(data: data, encoding: .utf8) { 24 print("Raw Response:") 25 print(responseString) 26 } 27 } 28 29 // Execute the task 30 task.resume() 31 dispatchGroup.wait() 32}

This code snippet demonstrates how to create a data task with URLSession to send a GET request. The response from the server is captured as raw data and printed for verification.

Handling Successful Requests (Status Code: 200)

After making a request, it's crucial to check the HTTP status code to verify a successful interaction. When the HTTP status code 200 is returned, we can confidently parse the JSON response using Swift's Codable protocol.

Swift
1if let url = URL(string: "\(baseURL)/todos") { 2 let dispatchGroup = DispatchGroup() 3 dispatchGroup.enter() 4 5 let task = URLSession.shared.dataTask(with: url) { data, response, error in 6 defer { dispatchGroup.leave() } 7 8 if let error = error { 9 print("Error occurred: \(error.localizedDescription)") 10 return 11 } 12 13 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { 14 print("Unexpected status code") 15 return 16 } 17 18 if let data = data { 19 do { 20 let todos = try JSONDecoder().decode([Todo].self, from: data) 21 print("Todos retrieved successfully:") 22 for todo in todos { 23 print("Title: \(todo.title), Description: \(todo.description), Done: \(todo.done)") 24 } 25 } catch { 26 print("Failed to decode JSON: \(error.localizedDescription)") 27 } 28 } 29 } 30 task.resume() 31 dispatchGroup.wait() 32} 33 34struct Todo: Codable { 35 let id: Int 36 let title: String 37 let description: String 38 let done: Bool 39}

By leveraging Swift’s Codable protocol, we can easily decode JSON data into a Swift structure.

Note: While a status code of 200 indicates success, it does not always guarantee that the response contains the expected data. Some APIs may return an empty array ([]) when no records are found or a subset of records due to pagination. In such cases, make sure to implement corresponding conditional checks.

Handling Bad Requests (Status Code: 400)

A 400 status code indicates a bad request, which could result from incorrect syntax. We handle this by checking the status code and printing an error message.

Swift
1guard let httpResponse = response as? HTTPURLResponse else { 2 return 3} 4 5if httpResponse.statusCode == 400 { 6 print("Bad Request. The server could not understand the request due to invalid syntax.") 7}
Handling Unauthorized Requests (Status Code: 401)

When encountering a 401 status code, it implies unauthorized access due to missing or invalid credentials. We address this by notifying the user accordingly.

Swift
1if httpResponse.statusCode == 401 { 2 print("Unauthorized. Access is denied due to invalid credentials.") 3}
Handling Not Found Errors (Status Code: 404)

A 404 status code signifies that the requested resource could not be found, which often results from an incorrect endpoint.

Swift
1if httpResponse.statusCode == 404 { 2 print("Not Found. The requested resource could not be found on the server.") 3}
Handling Internal Server Errors (Status Code: 500)

A 500 status code reflects an internal server error, signifying an issue on the server side.

Swift
1if httpResponse.statusCode == 500 { 2 print("Internal Server Error. The server has encountered a situation it doesn't know how to handle.") 3}
Handling Unexpected Status Codes

For other unexpected status codes, a generic approach allows handling them in a structured way.

Swift
1else { 2 print("Unexpected Status Code: \(httpResponse.statusCode)") 3}
Conclusion, Key Takeaways, and Next Steps

In this lesson, we've delved into using Swift’s URLSession to make GET requests to an API. We've explored how to retrieve and handle responses effectively, using HTTP status codes to interpret server communication. This knowledge is pivotal for creating reliable API interactions within your Swift applications. As you continue, practice experimenting with code snippets and handling different status codes to solidify your understanding. Future lessons will build upon this foundation, enabling more intricate tasks like updating and manipulating API data. Keep going strong as you advance through the course!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal