Creating a REST API Manager in Swift
REST APIs or RESTful web services in layman’s terms are the way that the mobile apps or websites communicate and transmit data to servers and vice-versa. There are a set HTTP Requests posting and getting data to and from servers over internet. In this article we will see how we can make a Swift class for calling them and keeping the various requests organised. As you may already know, Swift is the programming language used to develop iOS apps and there will be many times that as a mobile developer you will have to call REST APIs.
The REST API manager
The REST API Manager is a Singleton class. A Singleton is a design pattern that only allows the developer to instantiate a class to only one object. That’s very logical, as there will be no need to create a new instance of the manager each time you want to make an API call. This object will be accessible from everywhere in the app.
So, let’s begin.
After we create a new Xcode Single-View iOS project, we must create a new class of type NSObject.
The first thing we should do is to set the most important property of the API Manager, the base url of our API. For our convenience we will use a test REST API that is already online, it is called JSONPlaceholder (https://jsonplaceholder.typicode.com/) and it provides specific JSON responses for each call. We will also use the SwiftyJSON library which enables us to treat JSON responses as a specific data type and it can create and read JSON objects without serialising and deserialising JSON data. You can download SwiftyJSON from GitHub on this link https://github.com/SwiftyJSON/SwiftyJSON or install it via cocoapods.
So, we set a static string that will be the base url.
The code of the API Manager should now look as shown below:
class APIManager {
let baseURL = "https://jsonplaceholder.typicode.com"}
JSONPlaceholder responds to the endpoints below:
/posts
/comments
/albums
/photos
/todos
/users
The responses are some arrays of objects of each type.
For example if we want to get the first post we will make a GET request to
https://jsonplaceholder.typicode.com/posts/1 .
Next, we have to set a static property called sharedInstance that will return the one and only instance of our APIManager class each time called. We will also set a static string variable
called getPostsEndpoint which holds the value of the endpoint we want to call. It is a good practice to declare all the endpoints we may call in the top of our APIManager class.
The code should now look like this:
import SwiftyJSON
class APIManager {
let baseURL = "https://jsonplaceholder.typicode.com"
static let sharedInstance = APIManager()
static let getPostsEndpoint = "/posts/"
}
Now, it is time to create a function for each endpoint of the REST API we want to call.
Let’s see the function to get a specific post. The parameter that the endpoint receives is the id of the post we want to get and the syntax is: /posts/{id} .
So it would be very convenient for the use of the API Manager from other points of our app, to call that function with an integer id parameter. This function will also has two callbacks. On the first (onSuccess) we will get the JSON response from the REST API call if it succeeds, on the second callback we will get an Error object if the request fails.
The declaration of this function should look something like this:
func getPostWithId(postId: Int, onSuccess: @escaping(JSON) -> Void, onFailure: @escaping(Error) -> Void){
}
Now we have to call the endpoint we want.
In the function we declared above, we should add the following lines of code which make an HTTP Request.
let url : String = baseURL + APIManager.getPostsEndpoint + String(postId)
let request: NSMutableURLRequest = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "GET"
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if(error != nil){
onFailure(error!)
} else{
let result = JSON(data: data!)
onSuccess(result)
}
})
task.resume()
Now lets create a typical interface just to check that our function works as it should and it gets the data we want. So, we open the Main.storyboard file of our project and we drag a button and a text view. The view should now look like this.
Now open the main view controller file of your project. By default it should be named ViewController.swift. In the first line after the declaration of the class we should now declare a UITextView outlet by adding this line:
@IBOutlet weak var dataView: UITextView?
Then we should declare an IBAction which we will link to the button we added to our view.
This function will call the function of the APIManager which requests a post with ID from the REST API.
The declaration of IBAction should look like this:
@IBAction func getDataPressed(){ }
Now we are going back to Main.storyboard file to link the dataView UITextView to the UITextView we dragged to the view before and the getDataPressed() action to the “Touch Up Inside” event of the button we added on the view.
Now we are going back to ViewController.swift to implement the getDataPressed() function.
We add the following code in getDataPressed() function
APIManager.sharedInstance.getPostWithId(postId: 1, onSuccess: { json in
DispatchQueue.main.async {
self.dataView?.text = String(describing: json)
}
}, onFailure: { error in
let alert = UIAlertController(title: “Error”, message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: “Dismiss”, style: .default, handler: nil))
self.show(alert, sender: nil)
})
This code calls the getPostWithId function of our APIManager class. The first parameter is an Integer and it indicates the ID of the post that will be requested, the second is the callback function where we get the response of our request if it succeeds. The json variable contains a JSON object with the response which we then present on the UITextView of our main view. This operation must be in an async dispatch queue due to a complicated issue regarding auto layouts because the size of the UITextView slightly changes when the data is displayed. The third parameter is the failure callback where we show an alert to the user notifying him about the error.
The getDataPressed() function should now look like this:
@IBAction func getDataPressed(){
APIManager.sharedInstance.getPostWithId(postId: 1, onSuccess: { json in
DispatchQueue.main.async {
self.dataView?.text = String(describing: json)
}
}, onFailure: { error in
let alert = UIAlertController(title: “Error”, message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: “Dismiss”, style: .default, handler: nil))
self.show(alert, sender: nil)
})
}
Done!
Now you can run the app in iOS simulator. If you tap the button , after a while you will see the data returned from the API call displayed in the text view. If you turn off the Wi-fi of your computer and press the button you will see the error alert on iOS simulator.
You can find the complete project on my github.