Alamofire 5 Tutorial: Primeiros Passos Para Manipular Dados
Alamofire é uma biblioteca escrita totalmente na linguagem de programação Swift para realizar requisições HTTP para as plataformas iOS e macOS.
Ele não utiliza recursos do seu “antecessor” escrito em Objective-C - conhecido como AFNetworking. Ao contrário disso, ele encapsula boa parte do grupo de classes fornecida pela Apple para realizar requisições HTTP, classes como URLSession
.
Seu principal foco é fornecer uma interface com as principais tarefas para o desenvolvedor iOS realizar o tráfego de dados no aplicativo de forma simples e prática, ajudando-o a ter foco no que é mais importante, criar o aplicativo iOS.
Em resumo, o Alamofire fornece mecanismos para:
- Fazer upload de arquivos com multipart
- Buscar dados em uma API RESTful
- Baixar arquivos do servidor
Apesar do Alamofire facilitar a nossa vida em relação a camada de serviços do aplicativo, é interessante saber como essa conexão funciona em classes como URLSession. Isso vai te dar uma boa base de como um aplicativo e uma API RESTful funcionam.
Neste tutorial de Alamofire, você vai aprender a fazer as principais tarefas de comunicação de dados como upload de fotos e arquivos, requests e manipulação de dados em uma API RESTful.
Começando
O Aplicativo que vamos criar nesse tutorial sobre Alamofire será um aplicativo para fazer upload de imagens em um dos servidores do imgur. Esta é uma plataforma que armazena milhões de conteúdos (post) com imagens e videos.
Através da API Imgur podemos realizar todos os testes do Alamofire como uploads, busca de imagens, download e exclusão de imagens.
Então, neste aplicativo iOS, você vai poder selecionar uma foto do seu iPhone, adicionar um título a essa foto e por fim, buscar e visualizar todos os dados dessa foto como largura, altura e link da web.
Será um aplicativo bem simples justamente para termos o foco no Alamofire.
Para usar a biblioteca do Alamofire, vamos precisar do gerenciador de dependências Cocoapods.
Partindo do princípio que o cocoapods já se encontra instalado no seu MacOS, crie um novo projeto no Xcode e crie um Podfile
para o projeto (o nome do projeto que usei foi Alamofire Tutorial).
Para criar um Podfile, execute o comando
pod init
no terminal e no diretório do seu projeto
##Adicionando o Alamofire 5
Para adicionar o Alamofire no seu projeto, coloque a dependência no Podfile
que foi gerado. Após adicionar essa dependência, execute o comando pod install
.
1
2
3
4
target 'AlamofireTutorial' do
use_frameworks!
pod 'Alamofire', '5.0.0-beta.2'
end
Agora é só abrir o AlamofireTutorial.xcworkspace
e o Alamofire estará disponível no seu projeto para ser usado.
Não se esqueça que para trabalhar com o Alamofire devemos abrir o arquivo .xcworkspace e não o .xcodeproj
Antes de escrever as chamadas do Alamofire, vamos definir o layout do aplicativo e como vai funcionar os componentes.
Preparando o Aplicativo Para o Alamofire 5
Nas configurações padrões do seu projeto, exclua a main.storybaord
da seção Deployment info.
Se você já me acompanha (ou acompanha outros desenvolvedores iOS profissionais) deve saber que que os primeiros passos iniciais de um projeto é excluir a storyboard para ter total domínio na criação de layouts programaticamente, na linguagem de programação Swift. Logo, para que funcione esse método de criação do AutoLayout, precisamos avisar ao Xcode que não usaremos mais a main storyboard.
Agora abra o AppDelegate
e defina um layout que tenha uma UINavigationViewController com uma ViewController.
AppDelegate.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let rootViewController = ViewController()
let nav = UINavigationController(rootViewController: rootViewController)
window?.rootViewController = nav
window?.makeKeyAndVisible()
return true
}
}
Vamos precisar de uma imagem para realizar o upload, um botão para buscar a imagem no iPhone e um segundo botão - BarButton - que será o Enviar.
Para criar esses layouts, vamos usar o conceito de constraints com Anchor.
Copie o seguinte bloco de código e coloque como conteúdo da sua ViewController.
ViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton(type: .roundedRect)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Selecione a imagem", for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
button.backgroundColor = UIColor.black
button.addTarget(self, action: #selector(takePicture(_:)), for: UIControl.Event.touchUpInside)
button.setTitleColor(UIColor.white, for: .normal)
return button
}()
let imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
view.addSubview(button)
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Enviar", style: .plain, target: self, action: #selector(send(_:)))
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
imageView.widthAnchor.constraint(equalTo: view.widthAnchor),
imageView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
}
@objc func send(_ sender: UIBarButtonItem) {
}
@objc func takePicture(_ sender: UIButton) {
}
}
Você terá um layout como este:
Agora vamos adicionar a lógica para buscar a imagem no iPhone e mostrar na tela para o usuário.
Buscando Imagens Na Galeria
Dentro do método takePicture(_ sender: UIButton)
, adicione o seguinte bloco de código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@objc func takePicture(_ sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = false
if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.sourceType = .camera
} else {
picker.sourceType = .photoLibrary
picker.modalPresentationStyle = .fullScreen
}
present(picker, animated: true)
}
Em resumo, esse bloco vai buscar ou na câmera (caso esteja usando um iPhone real) ou na galeria como no caso do simulator iOS.
Além disso, precisamos definir que nossa ViewController está de acordo com o protocol UIImagePickerControllerDelegate
e UINavigationControllerDelegate
.
Adicione uma nova extension
no final do arquivo (após a classe) com esses protocolos para escutar os eventos quando uma imagem for selecionada.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { // 1
print("failure to handle info")
dismiss(animated: true)
return
}
imageView.image = image // 2
dismiss(animated: true)
}
}
- Aqui buscamos a imagem efetivamente no formatado data (blob)
- Adicionamos a imagem no ImageView definido no começo da classe.
Adicionando Título a Imagem
Após escolher a imagem e apertar em Enviar na barra de navegação, devemos abrir um alerta para digitar o título da imagem. O bloco de alerta deverá ser inserido dentro de func send(_ sender: UIBarButtonItem)
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@objc func send(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "", message: "Digite o título", preferredStyle: .alert)
let submit = UIAlertAction(title: "Submit", style: .default) { (action) in
if (alert.textFields?.count ?? 0 > 0) {
// bloco que envia a foto
}
}
alert.addAction(submit)
alert.addTextField { (textField) in
textField.placeholder = "Titulo"
}
present(alert, animated: true)
print("send")
}
Antes de iniciar as chamadas e lógicas referentes ao Alamofire precisamos criar uma conta no Imgur e verificar como deverá ser feita as chamadas na API RESTful.
Imgur API
Para dar continuidade nesse tutorial do Alamofire você precisará criar uma conta no Imgur.
Acesse a URL https://imgur.com/register e crie uma conta gratuita na plataforma Imgur.
Com a conta criada acesse a URL https://api.imgur.com/oauth2/addclient para criar uma app para se conectar com a API. Preencha os campos:
- Application Name
- Authorization type: pode manter sem uma url de callback
Depois de submeter o formulário, a plataforma irá gerar um ClientID para você usar nos cabeçalhos (header) das suas requisições para autorizar as suas chamadas.
Agora na URL https://apidocs.imgur.com/#de179b6a-3eda-4406-a8d7-1fb06c17cb9c temos várias definições de como fazer as chamadas na API para manipular os dados referentes as imagens.
Vamos fazer o nosso aplicativo iOS realizar o upload da imagem no endpoint https://api.imgur.com/3/image
com o método POST.
Alamofire 5: Upload de Imagens e Arquivos
Para o que o Alamofire fique disponível no seu projeto adicione o import logo abaixo do UIKit - import Alamofire
.
Agora no bloco de código onde ocorre o envio de texto vamos fazer a chamada para a função que irá realizar o upload do arquivo usando o Alamofire.
1
2
3
4
5
6
7
let submit = UIAlertAction(title: "Submit", style: .default) { (action) in
if (alert.textFields?.count ?? 0 > 0) {
if let image = self.imageView.image, let textField = alert.textFields?.first {
self.upload(image: image, text: textField.text ?? "") //. upload aqui!
}
}
}
E a função de upload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fileprivate func upload(image: UIImage, text: String) {
// 1
guard let imageData = image.jpegData(compressionQuality: 0.5) else {
print("Could not get JPEG representation of UIImage")
return
}
// 2
AF.upload(multipartFormData: { (formData) in
formData.append(imageData, withName: "image", fileName: "image.jpg", mimeType: "image/jpeg")
formData.append(text.data(using: String.Encoding.utf8)!, withName: "title")
}, to: "https://api.imgur.com/3/image", headers: ["Authorization": "Client-ID xxxxxx"]).responseJSON { (response) in
// 3
guard response.result.isSuccess, let value = response.result.value as? [String: Any] else {
print("Error \(String(describing: response.result.error))")
// completion error
return
}
// 4
guard let rows = value["data"] as? [String: Any] else {
print("Malformed data received from service")
// completion error
return
}
// 5
print("Link da imagem \(String(describing: rows["link"]))")
print("Titulo da imagem \(String(describing: rows["title"]))")
}
}
- Aqui pegamos uma representação da imagem no formato JPG.
- Realizamos o upload da imagem com o Alamofire (vamos detalhar mais esse passo).
- Verificamos se o response teve sucesso.
- Buscamos o objeto
data
do JSON - Imprimimos o título que definimos para a imagem e a URL da imagem no imagur.
A maioria das funções do Alamofire são estáticas, o que significa que podemos chamá-la sem uma instância. Com o AF. temos várias funções para manipular requisições HTTP.
A que usamos AF.upload
nos fornece uma closure para definir os parâmetros de um formData. É aqui onde acontece a mágica do upload de arquivos.
Já no parâmetro to
passamos o endpoint e em headers
as credenciais exigidas pelo imgur. Note que não precisamos declarar o verbo HTTP (POST) porque a função upload já possui esse valor atruibuido.
Por fim, o .responseJSON { (response) in }
é o bloco que responde os dados e propriedades fornecidas pelo servidor como status code, JSON e mais.
O response.result.value
é o JSON que precisamos parsear para obter os dados de resposta do servidor. Agora com o link da imagem pronta, podemos abrir uma nova ViewController e exibir a imagem do servidor se assim desejarmos.
Veja que no console, é exibida a URL da imagem no servidor da imager e também o título que passamos no campo de texto.
Buscando Dados com Alamofire 5
Agora vamos buscar as informações da nossa conta ao invés de publicar uma item como uma foto.
O processo é muito semelhante ao de upload de imagem com o Alamofire. Porém, agora iremos utilizar o método request
, que por padrão, já passará como método GET.
Crie uma nova ViewController chamada de AccountViewController.
De volta no AppDelegate.swift, vamos trocar para a nossa nova ViewController.
1
2
3
window = UIWindow(frame: UIScreen.main.bounds)
let rooViewController = AccountViewController()
Neste exemplo, vamos buscar o ID da nossa conta no Imgur. Você pode testar com outros endpoints da API se quiser, mas para começarmos de forma simples e efetiva, iremos usar o endpoint da conta - https://api.imgur.com/3/account/
Troque o para o nome de usuário da conta imgur.
A implementação dessa nova ViewController começará assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import UIKit
import Alamofire
class AccountViewController: UIViewController {
let titleView: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = .red
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(titleView)
NSLayoutConstraint.activate([
titleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
titleView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
getAccount { (id) in // 1
self.titleView.text = id // 2
}
}
}
- Este item será o método responsável por buscar os dados de uma conta.
- Este segundo item será a exibição da nossa resposta, onde iremos adicionar o ID no campo de texto criado anteriormente.
Como havia dito, o processo de busca é muito semelhante ao de envio. Ainda precisamos passar alguns parâmetros no header para autenticação.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fileprivate func getAccount(completion: @escaping (String) -> Void) {
AF.request("https://api.imgur.com/3/account/username", // 1
headers: ["Authorization": "Client-ID xxxxxxx"]).responseJSON { (response) in
guard response.result.isSuccess, let value = response.result.value as? [String: Any] else {
print("Error \(String(describing: response.result.error))")
// completion error
return
}
guard let rows = value["data"] as? [String: Any] else {
print("Malformed data received from service")
// completion error
return
}
print("URL \(String(describing: rows["url"]))")
print("ID \(String(describing: rows["id"]))")
DispatchQueue.main.async { // 2
completion(String(describing: rows["id"] ?? ""))
}
}
}
- Aqui você deve adicionar o nome de usuário da sua conta no Imgur. Todo o restante da resposta é muito semelhante ao de envio. Parsear o JSON e pegar os dados desejados.
- Neste item, delegamos a resposta para o completion e por último, mas não menos importante, adicionamos no campo de texto o ID da conta.
Você pode testar vários outros endpoints para se acostumar com o Alamofire.
Demos um grande passo nas requisições HTTP com o Alamofire. Acredito fortemente que será mais fácil buscar dados a partir de agora nos seus projetos e aplicativos iOS.
Maravilha! Em breve você receberá conteúdos exclusivos por e-mail. Continue lendo os artigos para aprender mais sobre programação.