Introduction
I am brazilian and we are facing a water crisis (specially in São Paulo state), the drought is the most severe in the history of São Paulo and the people become interested in knowing the levels in the Cantareira reservoir system.
The state water authority, SABESP, has a website that shows the current levels with daily updates.
An API (https://github.com/rafaell-lycan/sabesp-mananciais-api) was made based on the data available on this website and we can follow the level changes of each reservoir in the Cantareira reservoir system.
As usual, the source code of this post is available at my github.
API Details
My friend, William Bruno (https://github.com/wbruno), is a contributor of this API.
The API address is https://sabesp-api.herokuapp.com/.
Besides the name of the reservoir, it has the current level (volume armazenado), rainfall of the day (pluviometria do dia), accumulated rainfall of the month (pluviometria acumulada do mês) and historical average of the month (média histórica do mês).
The API returns the following JSON:
[
{
"name": "Cantareira",
"data": [
{
"key": "volume armazenado",
"value": "18,9 %"
},
{
"key": "pluviometria do dia",
"value": "16,4 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "206,3 mm"
},
{
"key": "média histórica do mês",
"value": "178,0 mm"
}
]
},
{
"name": "Alto Tietê",
"data": [
{
"key": "volume armazenado",
"value": "22,8 %"
},
{
"key": "pluviometria do dia",
"value": "7,3 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "186,4 mm"
},
{
"key": "média histórica do mês",
"value": "172,4 mm"
}
]
},
{
"name": "Guarapiranga",
"data": [
{
"key": "volume armazenado",
"value": "85,0 %"
},
{
"key": "pluviometria do dia",
"value": "11,2 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "200,6 mm"
},
{
"key": "média histórica do mês",
"value": "153,2 mm"
}
]
},
{
"name": "Alto Cotia",
"data": [
{
"key": "volume armazenado",
"value": "64,6 %"
},
{
"key": "pluviometria do dia",
"value": "1,0 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "150,4 mm"
},
{
"key": "média histórica do mês",
"value": "149,1 mm"
}
]
},
{
"name": "Rio Grande",
"data": [
{
"key": "volume armazenado",
"value": "97,3 %"
},
{
"key": "pluviometria do dia",
"value": "2,6 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "199,4 mm"
},
{
"key": "média histórica do mês",
"value": "186,3 mm"
}
]
},
{
"name": "Rio Claro",
"data": [
{
"key": "volume armazenado",
"value": "43,9 %"
},
{
"key": "pluviometria do dia",
"value": "4,0 mm"
},
{
"key": "pluviometria acumulada no mês",
"value": "235,2 mm"
},
{
"key": "média histórica do mês",
"value": "245,9 mm"
}
]
}
]
The returned data showed above are from the current day; if we want to get data of past days, we have to send the desired date at the URL of the API, i.e. https://sabesp-api.herokuapp.com/2015-01-01.
Creating the iOS Project
It is not my goal teach how to develop in iOS, though you can get the project at https://github.com/rpresb/ios-mananciais-sabesp and if you have any issue leave your comment below.
Below are the screenshots with the options I chose to create the project.
First of all, choose the project template Single View Application and click Next
In the next window, fill the fields Product Name, Organization Name, Organization Identifier, Language and Devices.
Remember to uncheck the Use Core Data checkbox and select the Swift language.
Coding
Although is not my intention in this post to teach you how to develop in Swift, I will try to explain the following code that I made for this app.
Feel free to download the code at https://github.com/rpresb/ios-mananciais-sabesp.
The file Manancial.swift
import Foundation
struct Manancial {
var name:String
var volume:String
var rainDay:String
var rainMonth:String
var rainAvg:String
init(manancialDic:NSDictionary) {
NSLog("%@", manancialDic);
name = manancialDic["name"] as String
var data = manancialDic["data"] as NSArray
volume = data[0]["value"] as String
rainDay = data[1]["value"] as String
rainMonth = data[2]["value"] as String
rainAvg = data[3]["value"] as String
}
}
In this file we have the structure that will be used to manipulate the data that we get from the API.
In the constructor of the Manancial class, we receive a NSDictionary
with the JSON returned from the API.
For each reservoir (manancial), we have an array named data which has 4 elements.
Considering that the elements are always returned in the same order, we can use the hard-coded indexes to set the data volume
, rainDay
, rainMonth
e rainAvg
.
File ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var volumeLabel: UILabel!
@IBOutlet weak var dayLabel: UILabel!
@IBOutlet weak var monthLabel: UILabel!
@IBOutlet weak var avgLabel: UILabel!
@IBOutlet weak var page:UIPageControl!
@IBOutlet weak var level:UIView!
var mananciais: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = "Carregando..."
volumeLabel.hidden = true
dayLabel.hidden = true
monthLabel.hidden = true
avgLabel.hidden = true
level.hidden = true
loadData();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadData() {
let baseURL = NSURL(string: "https://sabesp-api.herokuapp.com/")
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(baseURL!, completionHandler: { (location: NSURL!, response:NSURLResponse!, error: NSError!) -> Void in
if (error == nil) {
let dataObject = NSData(contentsOfURL: location)
self.mananciais = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as NSArray
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.page.numberOfPages = self.mananciais.count
self.showPage(0)
})
} else {
NSLog("%@", error)
let networkIssueController = UIAlertController(title: "Error", message: "Unable to load data. Connectivity error!", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
networkIssueController.addAction(okButton)
self.presentViewController(networkIssueController, animated: true, completion: nil)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.nameLabel.text = "Erro"
self.volumeLabel.hidden = true
self.dayLabel.hidden = true
self.monthLabel.hidden = true
self.avgLabel.hidden = true
self.level.hidden = true
})
}
})
downloadTask.resume()
}
func showPage(index:Int) {
let manancial = Manancial(manancialDic: self.mananciais[index] as NSDictionary)
self.nameLabel.text = "\(manancial.name)"
self.volumeLabel.text = "\(manancial.volume)"
self.dayLabel.text = "\(manancial.rainDay)"
self.monthLabel.text = "\(manancial.rainMonth)"
self.avgLabel.text = "\(manancial.rainAvg)"
self.volumeLabel.hidden = false
self.dayLabel.hidden = false
self.monthLabel.hidden = false
self.avgLabel.hidden = false
self.level.hidden = false
var volume:NSDecimalNumber = NSDecimalNumber(string: manancial.volume.stringByReplacingOccurrencesOfString(" %", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil).stringByReplacingOccurrencesOfString(",", withString: ".", options: NSStringCompareOptions.LiteralSearch, range: nil))
var pixel = CGFloat(volume) / 100 * self.view.frame.size.height
let y:CGFloat = self.view.frame.size.height - pixel;
self.level.frame.origin.y = y
self.level.frame.size.height = pixel
}
@IBAction func pageChanged() {
showPage(page.currentPage);
}
}
In the function viewDidLoad
we call the function loadData
which is responsible to request the data in the API.
After get the JSON with the data, we serialize it into a NSArray
object.
This object is our data source to show the reservoir information and create a page for each reservoir.
Each element in this array is a NSDictionary
which we will send to the function showPage
when the user changes the page.
The next step is to get the first page; we have to send the first element of the array to the function showPage
.
The running app is shown below
Conclusion
We have worked with another interesting API.
I chose iOS with Swift to work with this API to show that, even with a very simple API we can make a creative usage to offer an interesting experience to the user.
Although is not my goal to teach how to develop in Swift, this could be a starting point to you understand how it works and get excited to go deeply in the iOS development.
If you have any question, issue or suggestion, leave your comment below that I will answer with pleasure.