Introdução
Um problema bastante atual (pelo menos em São Paulo) é a falta d’água. Esse é um problema crônico, porém teve muito impacto no final do ano passado e início deste ano.
Uma API foi criada para que possamos acompanhar a situação de cada um dos mananciais da SABESP.
Detalhes da API
Essa dica foi enviada pelo meu amigo William Bruno.
O endereço da API é https://sabesp-api.herokuapp.com/, e nela temos algumas informações sobre os mananciais da SABESP.
Além do nome do manancial, obtemos os dados de volume armazenado, pluviometria do dia, pluviometria acumulada do mês e média histórica do mês.
A API retorna o seguinte 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" } ] } ]
Os dados obtidos na requisição acima são do dia em que escrevi este texto – 31 de março; caso queiramos obter dados de dias anteriores, precisamos passar a data que desejamos na URL da API, como por exemplo https://sabesp-api.herokuapp.com/2015-01-01.
Iniciando o Projeto iOS
Eu sempre gosto de testar as APIs com projetos onde posso imaginar uma aplicação real.
Para os testes de hoje, escolhi o iOS como destino da aplicação.
Não é meu objetivo ensinar a programar em iOS, porém você pode baixar o projeto neste link e, se tiver alguma dúvida, eu terei o prazer em ajudar.
Abaixo estão as telas com as configurações que escolhi na criação do projeto.
Primeiro, escolha a template para seu projeto – nesse caso, Single View Application, e clique em Next.
Na próxima tela, preencha as informações solicitadas para Product Name, Organization Name, Organization Identifier, Language e Devices.
Lembre-se de deixar desmarcada a opção Use Core Data e de selecionar a linguagem Swift.
Codificando
Apesar de não ser a minha intenção neste artigo ensinar o desenvolvimento em Swift, vou tentar explicar os códigos utilizados.
O projeto inteiro está disponível no meu GitHub, fique à vontade para duplicar, melhorar e contribuir.
Arquivo 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 } }
Neste arquivo, temos a estrutura que será utilizada para facilitar a manipulação dos dados obtidos da API.
No construtor dessa classe, recebemos um NSDictionary que tem o JSON da API.
Para cada manancial, temos um array chamado data que possui 4 elementos.
Considerando que os elementos sempre serão apresentados na mesma ordem, podemos utilizar os índices fixos para obter os dados de volume, rainDay, rainMonth e rainAvg.
Arquivo 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() // Dispose of any resources that can be recreated. } 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); } }
Na função viewDidLoad, é chamada uma função loadData que é responsável por solicitar os dados para a API.
Após obter o JSON com o resultado da API, fazemos a serialização dele para um objeto do tipo NSArray.
Esse objeto será nossa fonte de dados para executar a paginação e apresentar os dados da tela.
Cada elemento desse array é um NSDictionary que será enviado para a função showPage quando necessário.
O próximo passo é obter a primeira página e, para isso, passamos o primeiro elemento do array para a função showPage. Essa função tem o código para mostrar os dados do manancial na tela.
O resultado final da app podemos ver na imagem abaixo:
Conclusão
Conhecemos mais uma API interessante que podemos explorar em muitos formatos.
Decidi usar o iOS com Swift para mostrar que, mesmo com uma API simples, podemos fazer um uso criativo e entregar uma experiência interessante para o usuário.
Ainda que não seja meu objetivo ensinar a programar em Swift, esse pode ser um ponto de partida, pois é simples e não tem muita dificuldade para começar a ver algum resultado.
Caso tenha alguma dúvida, crítica ou sugestão, envie sua mensagem que eu responderei com prazer.