RESTful APIs
REST (Representational State Transfer) is an architectural approach for creating Web APIs based on the HTTP protocol. This approach takes into account certain key features, such as access to resources via specific URLs (Endpoint URLs) and the use of data formats such as JSON or XML. RESTful APIs are stateless, which means that each client request to the server must contain all the necessary information, without relying on a session state maintained by the server.
HTTP Protocol: HTTP (HyperText Transfer Protocol) is the main communication protocol used on the Web for transferring information. It is based on a system of requests and responses between a client and a server. When a client (such as a Web browser or an app) wants to access a resource on the server, it sends an HTTP request. The server processes the request and sends an HTTP response to the client.
Structure of an HTTP Request: An HTTP request consists of four main parts:
- Request Line: It contains the HTTP method used in the request, the URL of the resource, and the HTTP protocol version. For example, GET http://www.example.com/api/users HTTP/1.1.
- Header Section: Includes any additional information about the request, such as content types accepted, authentication, and more.
- Empty Line: Marks the end of the header information in the request.
- Body: Contains the body of the message, which is optional data used in some types of requests, such as POST or PUT.
HTTP methods and the RESTful API: One of the key features of the RESTful API is the use of HTTP methods to specify the actions you want to perform on resources. Common methods include:
- GET: Used to obtain a resource from the server. For example, requesting the details of a specific user from an application that implements a RESTful API to manage users.
- POST: Used to create new resources on the server. For example, sending a new user’s details to the server to register a new account.
- PUT / PATCH: Used to update existing resources on the server. For example, update the information of an existing product in the database.
- DELETE: Used to delete a resource from the server. For example, delete a post from a social media application.
Structure of an HTTP Response: An HTTP response is similar to a request and includes:
- Status Line: Contains the status code of the response, indicating the success or failure of the request. For example, HTTP/1.1 200 OK indicates that the request was completed successfully.
- Header Section: Contains additional information about the response, such as content types, content size, and more.
- Empty Line: Marks the end of the header information in the response.
- Body: Contains the body of the message, which is optional data used to provide the actual response, such as a JSON containing the requested data.
Thus, RESTful APIs use the HTTP protocol to provide a standardized interface for accessing resources on a server via specific URL endpoints. HTTP requests and responses follow a well-defined structure that allows clients and servers to communicate effectively with each other, enabling operations such as obtaining, creating, updating, and deleting resources within the system.
Below is a simple example of how a network call can be constructed; you can also find the project on GitHub.
Step 1: Construction of a Dummy UI.
In this step, the first task is to create a test UI, using dummy data. This helps you understand what data you are working with and visualize what the UI will look like. A practical example would be to create user profile pages similar to those on GitHub, where we show the profile picture, username, and biography.
Step 2: Model Creation.
In this step, we will focus on creating the data models that we will use to represent the information obtained from the JSON response of our RESTful API.
GitHubUser: We will create a Swift structure called GitHubUser, which represents a GitHub user. To determine what properties to include in the structure, we will examine the test user interface and identify the necessary information, namely the profile picture, username, and biography.
Codable Protocol: To simplify data conversion between JSON and Swift, we will use the Codable protocol. This protocol combines two separate protocols, Encodable and Decodable, to encode (convert to JSON) and decode (convert from JSON) data. In this way, we can easily translate JSON data into our GitHubUser structure and vice versa.
Snake Case and Camel Case: JSON files usually use a notation style called “Snake Case,” where words are separated by underscores (e.g. avatar_url). On the other hand, in Swift, the usual style is called “Camel Case,” where the second and subsequent words begin with a capital letter (e.g. avatarUrl). We need to pay attention to this difference when defining properties in the Swift model and when working with JSON data.
Then we can distinguish the: Lower Camel Case (the one used for variables) and the Upper Camel Case (when we are dealing with the name of a struct and it also starts the first word with a capital letter).
To make it easier to match property names, we can use the .convertFromSnakeCase property of the JSON decoder, which automatically converts JSON key names from Snake Case to Camel Case during decoding.
In this way, we can create a custom decoder for our model, ensuring that properties are decoded correctly even when the JSON key names are in Snake Case. This allows us to get consistent and usable data from our server and Swift UI.
Step 3: Writing the Network Code.
In this step, we will address the implementation of the network code to get the data from the RESTful API. We will use a function called getUser() and mark it as async, which allows us to execute it asynchronously (without blocking the main core of the application) and with throws, so as to handle any errors that might occur during the network call.
Endpoint and URLSession: Before we start, we need the corresponding URL to get the data. We will use the GitHub API to get the user details. We need to create an instance of the network session in Swift using URLSession, which allows us to make network requests.
GET request — Download Data: In our case, we will make a “GET” type request, which is one of the four main types of network requests. The URLSession.shared.data(from: URL) method allows us to download data from the provided URL. This is a read operation, where we get the data without changing anything on the server.
Connection error handling: Since network calls can generate errors, we need to handle any scenarios where the URL might be incorrect or the API might not be reachable. In case of an invalid URL, we throw a custom error GHError.invalidURL. Handling errors appropriately is critical to providing appropriate feedback to the user in case of connection problems.
HTTP response handling: HTTP responses may contain status codes such as 404 (Not Found), 500 (Server Error), or 200 (OK). It is important to be specific about errors to understand what went wrong during the network call. In our case, if we get a response with code 200 (OK), everything is fine, otherwise we throw a specific error GHError.invalidResponse.
Decoding JSON data: Once we get the data from the network call, we need to decode it from the JSON format into the GitHubUser model object. To do this, we use a JSONDecoder. This decoder must be configured to handle the conversion between the “Snake Case” notation styles used in JSON and “Camel Case” notation styles used in Swift. Fortunately, Swift provides a built-in feature to handle this conversion automatically.
Decoding error handling: The decoding of JSON data may fail for various reasons, such as if the JSON does not match the expected structure. In such a case, we throw a custom error GHError.invalidData to inform the user of what is happening.
Properly dealing with errors and providing appropriate feedback to the user is essential to improve the user experience and make the application more reliable. By properly implementing error handling, we can ensure that the application works consistently and provides useful feedback in case of connection problems or unexpected data from the RESTful API.
Step 4: Connecting with the User Interface.
In this step, we will put together the network code we wrote earlier and connect it to the user interface to display the data obtained from the RESTful API.
The View and the Load Function: Within the View, we create a variable @State of type GitHubUser? that will contain the user data to be displayed. To make the network call, we will use the .task modifier, which allows us to execute an asynchronous function before the View is displayed. In this case, we assign the getUser() function the task of loading the user data. Since the function is asynchronous, we use await to wait for the completion of the network call, and try to handle any errors that might occur during execution.
Error Handling: To handle errors that may occur during the network call, we use the do-try-catch block. Within the do block, we try to get the user data via the getUser() function, and if the operation is successful, we assign the data to the user variable. In case the operation is unsuccessful, we catch the error in the catch block. We can add more catch blocks to handle specific types of errors if necessary.
Updating the User Interface: Once we have obtained the user data, we can update the user interface to display the results. For example, we can display the user’s name using the syntax user?.login ?? “Login Placeholder”. If there is no data, we provide a default value via Login Placeholder.
Avatar URL management: To display the user’s profile image, we use an asynchronous image view. We pass the user’s avatar URL to the image view using user?.avatarUrl ?? “”. In case the URL is null or an error occurs while loading the image, we will display a placeholder image, such as a gray circle.
In this way, we have completed the connection between the network code and the user interface. Now, when the user accesses the application, the user’s data will be loaded through the network call and displayed in the user interface in order to provide a complete and informative user experience.
For this article, I was greatly inspired by a YouTube video.
Appreciate your time spent reading! Should you desire further knowledge, don’t hesitate to reach out to me via annaceglia00@gmail.com or connect with me on LinkedIn.