How to make a RESTful app with Siesta
Learn how you can use the Siesta framework to improve your API interactions, networking, model transformations, caching, loading indications and more. By Sanket Firodiya.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to make a RESTful app with Siesta
20 mins
- Getting Started
- Yelp API
- Making a Network Request in Siesta
- Siesta Service
- Resource and ResourceObserver
- Adding the Spinner
- Siesta Transformers
- Restaurant Model
- Model Mapping
- RestaurantListViewController
- Building the Restaurant Details Screen
- RestaurantDetails Model
- Model Mapping
- Setting up Siesta in RestaurantDetailsViewController
- Handling the Navigation to RestaurantDetailsViewController
- Where to Go From Here?
Fetching data over the network is one of the most common tasks in mobile apps. Therefore, it’s no surprise that networking libraries such as AFNetworking and Alamofire are some of the most popular libraries among iOS developers.
However, even with those libraries, you still must write and manage a lot repetitive code in an app simply to fetch and display data from the network. Some of these tasks include:
- Managing duplicate requests.
- Canceling requests that are no longer needed, such as when user exits a screen.
- Fetching and processing data on background thread, and updating the UI on the main thread.
- Parsing responses and converting them into model objects.
- Showing and hiding loading indicators.
- Displaying data when it arrives.
Siesta is a networking library that automates this work and reduces the complexity of your code involved in fetching and displaying data from the network.
By adopting a resource-centric approach, rather than a request-centric one, Siesta provides an app-wide observable model of a RESTful resource’s state.
Getting Started
In this tutorial, you’ll build Pizza Hunter, an app that lets users search for pizza restaurants around them.
Warning: you might feel hungry by the end of this tutorial!
Use the Download Materials button at the top or bottom of this tutorial to download the starter project.
Open the PizzaHunter.xcworkspace project, then build and run. You’ll see the following:
The app already contains two view controllers:
-
RestaurantsListViewController
: Displays a list of pizza restaurants at the selected location. -
RestaurantDetailsViewController
: Displays details about the selected restaurant.
Since the controllers aren’t connected to any data source, the app just shows a blank screen right now.
Yelp API
You’ll be using the Yelp API to search for pizza restaurants in a city.
This is the request you’ll make to get a list of pizza restaurants:
GET https://api.yelp.com/v3/businesses/search
The JSON response to this request looks like this:
{ "businesses": [ { "id": "tonys-pizza-napoletana-san-francisco", "name": "Tony's Pizza Napoletana", "image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/d8tM3JkgYW0roXBygLoSKg/o.jpg", "review_count": 3837, "rating": 4, ... }, { "id": "golden-boy-pizza-san-francisco", "name": "Golden Boy Pizza", "image_url": "https://s3-media3.fl.yelpcdn.com/bphoto/FkqH-CWw5-PThWCF5NP2oQ/o.jpg", "review_count": 2706, "rating": 4.5, ... } ] }
Making a Network Request in Siesta
The very first step is to create a class named YelpAPI
.
Choose File ▸ New ▸ File from the menu, select Swift file and click Next. Name the file YelpAPI.swift, then Create. Replace the file’s contents with the following:
import Siesta
class YelpAPI {
}
This imports Siesta and creates the class stub for YelpAPI
.
Siesta Service
You can now flesh out the code needed to make an API request. Inside the YelpAPI
class, add the following:
static let sharedInstance = YelpAPI()
// 1
private let service = Service(baseURL: "https://api.yelp.com/v3", standardTransformers: [.text, .image, .json])
private init() {
// 2
LogCategory.enabled = [.network, .pipeline, .observers]
service.configure("**") {
// 3
$0.headers["Authorization"] =
"Bearer B6sOjKGis75zALWPa7d2dNiNzIefNbLGGoF75oANINOL80AUhB1DjzmaNzbpzF-b55X-nG2RUgSylwcr_UYZdAQNvimDsFqkkhmvzk6P8Qj0yXOQXmMWgTD_G7ksWnYx"
// 4
$0.expirationTime = 60 * 60 // 60s * 60m = 1 hour
}
}
Here’s a step-by-step explanation of the above code:
- Each API service is represented by a
Service
class in Siesta. Since Pizza Hunter needs to talk to only one API — Yelp — you only need oneService
class. - Tell Siesta about the details you want it to log to the console.
- Yelp’s API requires clients to send their access token in every HTTP request header for authorization. This token is unique per Yelp account. For this tutorial, you may use this one or replace it with your own.
- Set the
expirationTime
to 1 hour, since restaurant review data won’t change very often.
Next, create a helper function in the YelpAPI
class that returns a Resource
object:
func restaurantList(for location: String) -> Resource {
return service
.resource("/businesses/search")
.withParam("term", "pizza")
.withParam("location", location)
}
This Resource
object will fetch a list of pizza restaurants at the given location and make them available to any object that observes them. RestaurantListViewController
will use this Resource
to display the list of Restaurants in a UITableView
. You’ll wire that up now so you can see Siesta in action.
Resource and ResourceObserver
Open RestaurantListViewController.swift and import Siesta at the top:
import Siesta
Next, inside the class, create an instance variable named restaurantListResource
:
var restaurantListResource: Resource? {
didSet {
// 1
oldValue?.removeObservers(ownedBy: self)
// 2
restaurantListResource?
.addObserver(self)
// 3
.loadIfNeeded()
}
}
When the restaurantListResource
property is set, you do the following things:
- Remove any existing observers.
- Add
RestaurantListViewController
as an observer. - Tell Siesta to load data for the resource if needed (based on the cache expiration timeout).
Since RestaurantListViewController
is added as an observer, it also needs to conform to the ResourceObserver
protocol. Add the following extension at the end of the file:
// MARK: - ResourceObserver
extension RestaurantListViewController: ResourceObserver {
func resourceChanged(_ resource: Resource, event: ResourceEvent) {
restaurants = resource.jsonDict["businesses"] as? [[String: Any]] ?? []
}
}
Any object that conforms to the ResourceObserver
protocol will get notifications about updates to the Resource
.
These notifications will call resourceChanged(_:event:)
, passing the Resource
object that was updated. You can inspect the event
parameter to find out more about what was updated.
You can now put restaurantList(for:)
that you wrote in YelpAPI
class to use.
currentLocation
, the property on RestaurantListViewController
, gets updated when user selects a new location from the drop down.
Whenever that happens, you should also update the restaurantListResource
with the newly selected location. To do so, replace the existing currentLocation
declaration with the following:
var currentLocation: String! {
didSet {
restaurantListResource = YelpAPI.sharedInstance.restaurantList(for: currentLocation)
}
}
If you run the app now, Siesta will log the following output in your console:
Siesta:network │ GET https://api.yelp.com/v3/businesses/search?location=Atlanta&term=pizza Siesta:observers │ Resource(…/businesses/search?location=Atlanta&term=pizza)[L] sending requested event to 1 observer Siesta:observers │ ↳ requested → <PizzaHunter.RestaurantListViewController: 0x7ff8bc4087f0> Siesta:network │ Response: 200 ← GET https://api.yelp.com/v3/businesses/search?location=Atlanta&term=pizza Siesta:pipeline │ [thread ᎰᏮᏫᎰ] ├╴Transformer ⟨*/json */*+json⟩ Data → JSONConvertible [transformErrors: true] matches content type "application/json" Siesta:pipeline │ [thread ᎰᏮᏫᎰ] ├╴Applied transformer: Data → JSONConvertible [transformErrors: true] Siesta:pipeline │ [thread ᎰᏮᏫᎰ] │ ↳ success: { businesses = ( { categories = ( { alias = pizza; title = Pizza; } ); coordinat… Siesta:pipeline │ [thread ᎰᏮᏫᎰ] └╴Response after pipeline: success: { businesses = ( { categories = ( { alias = pizza; title = Pizza; } ); coordinat… Siesta:observers │ Resource(…/businesses/search?location=Atlanta&term=pizza)[D] sending newData(network) event to 1 observer Siesta:observers │ ↳ newData(network) → <PizzaHunter.RestaurantListViewController: 0x7ff8bc4087f0>
These logs give you some insight into what tasks Siesta is performing:
- Kicks off the GET request to search for pizza places in Atlanta.
- Notifies the observer i.e.
RestaurantListViewController
about the request. - Gets the results with response code 200.
- Converts raw data into JSON.
- Sends the JSON to
RestaurantListViewController
.
You can set a breakpoint in resourceChanged(_:event:)
in RestaurantListViewController
and type
po resource.jsonDict["businesses"]
in the console to see the JSON response. You’ll have to skip the breakpoint once as resourceChanged
is called when the observer is first added, but before any data has come in.
To display this restaurant list in your tableView, you need to reload the tableView when restaurants
property is set. In RestaurantListViewController
, replace the restaurants
property with:
private var restaurants: [[String: Any]] = [] {
didSet {
tableView.reloadData()
}
}
Build and run your app to see it in action:
Hurray! You just found yourself some delicious pizza. :]