Este artigo é uma continuação do artigo
Interagindo com o servidor por meio de uma API no iOS no Swift 3. Parte 1 e atualizando um
artigo antigo escrito em Objective-C no swift 3.
Breve teoria
Solicitação GET
GET é o método mais simples de solicitação de HTTP e é o que o navegador usa para carregar páginas da web. É usado para solicitar conteúdo localizado em um URL específico. O conteúdo pode ser, por exemplo, uma página da web, imagem ou arquivo de áudio. Por convenção, as solicitações GET são somente leitura e, de acordo com o padrão W3C, não devem ser usadas em operações que alteram o lado do servidor. Por exemplo, não usaremos uma solicitação GET para enviar um formulário ou enviar uma foto, porque essas operações exigem algumas alterações no lado do servidor (usaremos o POST nesses casos).
Pedido POST
O POST envia dados para processamento adicional ao URL. Os parâmetros estão incluídos no corpo da solicitação usando o mesmo formato que GET. Por exemplo, se quisermos postar um formulário contendo dois campos, um nome e uma idade, enviaremos algo semelhante a name = Martin & age = 29 no corpo da solicitação.
Este método de envio de parâmetros é amplamente utilizado em páginas da web. Os casos mais populares são formulários. Quando preenchermos o formulário no site e clicar em Enviar, provavelmente a solicitação será POST.
Em nosso aplicativo, usamos o POST para classificar piadas. Enviaremos votos (ou +1 ou -1) para o servidor remoto.
Os dados da solicitação POST podem ser estruturados usando diferentes formatos. Os parâmetros geralmente são formatados de acordo com os padrões de codificação de URL de formulário (de acordo com o padrão HTML do W3C). Esse é o formato padrão e é amplamente usado em muitos navegadores. Nosso método aceita o dicionário Dictionary como argumento, mas não podemos enviar o dicionário Dictionary pela conexão HTTP porque é um tipo Swift interno. Para encaminhar através de uma conexão HTTP, precisamos criar uma representação reconhecível do dicionário. É como se comunicar com um estrangeiro. Traduzimos nossa mensagem para um idioma universal, e ela se traduz de um idioma universal para seu idioma nativo. A linguagem universal no HTTP é o padrão W3C, nossa linguagem é Swift, a linguagem do destinatário é desconhecida para nós.
O padrão W3C define regras que definem o que significa uma representação reconhecível para cada caso. No nosso caso, precisamos apresentar os parâmetros após a parte do padrão codificada por URL (por exemplo, param1 = var1 e param2 = var2).
Webview
Usando ferramentas de linguagem de programação, podemos enviar solicitações para um servidor remoto. É o que os navegadores fazem antes de exibir uma página da web. A diferença está apenas no conteúdo da resposta. As páginas da Web são formatadas usando o padrão HTML, que define um conjunto de regras sobre como definir graficamente várias tags de marcação. Essas regras parecem simples, mas exibir uma página inteira seguindo o padrão W3C é uma tarefa difícil. Felizmente, o iOS possui um componente UIWebView interno que usa o conhecido mecanismo WebKit e interpreta HTML / CSS / JavaScript e exibe páginas da Web inteiras dentro de um UIView.
Existem vários casos em que queremos controlar o fluxo de navegação. Por exemplo, queremos saber quando determinado conteúdo ou um URL específico é carregado.
Ou talvez estejamos implementando um navegador seguro para crianças, queremos impedir que o usuário carregue páginas que se enquadram em determinados critérios, como sexo ou drogas. Para todos esses tipos de personalização, criamos uma instância de uma classe que implementa o protocolo UIWebViewDelegate como um representante da UIWebView. Podemos implementar os seguintes métodos:
webView:shouldStartLoadWithRequest:navigationType: webViewDidStartLoad: webViewDidFinishLoad: webView:didFailLoadWithError:
Usando o primeiro método, podemos controlar o fluxo de navegação permitindo ou bloqueando solicitações específicas. Os outros três métodos são eventos informativos (os nomes dos métodos dão uma boa idéia do evento).
Implementação da votação via solicitação POST
Adicionaremos os
arquivos MainViewController.swift e
HTTPCommunication.swift e criaremos um novo arquivo
WebViewController.swift no qual definiremos nosso webView.
Primeiro, crie um novo arquivo WebViewController.swift e defina a classe WebViewController nele:
WebViewController.swift import UIKit class WebViewController: UIViewController { } extension WebViewController { }
Agora, implementamos a funcionalidade para fazer solicitações POST na classe responsável por todas as nossas operações HTTP: classe HTTPCommunication. Para fazer isso, adicione um novo método postURL (_: params: completationHandler :) no HTTPCommunication.swift, que é semelhante ao método retrieveURL anterior (_: complementHandler :).
HTTPCommunication.swift class HTTPCommunication: NSObject { … func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { … }
Código do método
func postURL(_ url: URL, params: [String: Int], completionHandler: @escaping ((Data) -> Void)) { // (K.1) self.completionHandler = completionHandler // (K.2) func postBody(params: [String: Int]) -> Data? { // (K.3) var paramsArr: [String] = [] for (key, value) in params { paramsArr.append("\(key)=\(value)") } let postBodyString: String = paramsArr.joined(separator: "&") let postBodyData: Data? = postBodyString.data(using: .utf8) return postBodyData } var request: URLRequest = URLRequest(url: url) // (K.4) request.httpMethod = "POST" request.httpBody = postBody(params: params) let session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) // (K.5) let task: URLSessionDownloadTask = session.downloadTask(with: request) task.resume() }
(K.1) Com o fechamento, trabalharemos fora dessa função, portanto, denotamos-na com @escaping.
(K.2) Armazenamos o fechamento aprovado na propriedade conclusãoHandler.
(K.3) Escrevemos um método que converte o dicionário de parâmetros passados em forma de string e, em seguida, codifica essa string no tipo de dados na codificação utf8.
(K.4) Criamos uma solicitação com a URL passada e a configuramos: defina POST como o método e faça nossos parâmetros do tipo Data com utf8 como o corpo da solicitação.
(K.5) Por fim, crie uma sessão com uma configuração padrão e um representante como nós. E criamos uma tarefa para carregar o resultado dessa solicitação no armazenamento temporário. No final, descongele a tarefa.
Agora vamos adicionar a interface necessária ao MainViewController.swift. Adicione dois botões para votar voteUp e voteDown, que respectivamente aumentarão ou diminuirão a classificação da piada atual. Também adicionamos o botão "Chuck Who?", Que preencheremos com a funcionalidade na seção na visualização da web.
No MainViewController.swift, insira o código de inicialização voteUpButton, voteDownButton e chuckWhoButton.
MainViewController.swift lazy var jokeLabel: UILabel! = … var jokeID: Int! lazy var activityView: UIActivityIndicatorView! = …
Código de inicialização do botão
lazy var voteUpButton: UIButton! = { // (L.1) let button: UIButton = UIButton(type: .system) // (L.2) button.setTitle("Vote Up", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteUp), for: .touchUpInside) // (L.3) self.view.addSubview(button) // (L.4) return button // (L.5) }() lazy var voteDownButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Vote Down", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteDown), for: .touchUpInside) self.view.addSubview(button) return button }() lazy var chuckWhoButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Chuck Who?", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.chuckWho), for: .touchUpInside) self.view.addSubview(button) return button }()
O código de inicialização para todos os três botões é o mesmo.
(L.1) Declaramos uma variável lenta e inicializamos com um fechamento.
(L.2) Dentro do fechamento, definimos uma variável local para o botão, um tipo de sistema, especificamos o nome e o tamanho.
(L.3) Associamos ao botão a ação correspondente quando pressionado.
(L.4) Adicione à hierarquia de visualizações.
(L.5) E atribuímos essa variável local à nossa variável lenta.
Em seguida, em MainViewController.swift, insira o código do método para os botões
MainViewController.swift class MainViewController: UIViewController { … func retrieveRandomJokes() { … }
Código do método para botões
func voteUp() { let http: HTTPCommunication = HTTPCommunication() // (M.1) let params: [String: Int] = ["joke_id": self.jokeID, "vote": 1] // (M.2) if let url = URL(string: "http://example.com/rater/vote") { // (M.3) http.postURL(url, params: params) { (data) in // (M.4) print("\(data). Voted Up") } } } func voteDown() { let http: HTTPCommunication = HTTPCommunication() let params: [String: Int] = ["joke_id": self.jokeID, "vote": -1] if let url = URL(string: "http://example.com/rater/vote") { http.postURL(url, params: params) { (data) in print("\(data). Voted Down") } } } func chuckWho() { }
O código dos métodos para votar para cima e para baixo é o mesmo.
(M.1) Crie um objeto de classe para comunicação com o servidor.
(M.2) Criamos um dicionário de parâmetros que inclui o id da piada em que estamos votando, e o valor real do voto é +1 ou -1.
(M.3) Em seguida, obtemos o URL com segurança na barra de endereços do nosso domínio de treinamento
http://example.com/rater/vote.
(M.4) E enviamos através do POST uma solicitação para url de nossos parâmetros. E imprima no console uma linha indicando a conclusão da solicitação.
Em seguida, em MainViewController.swift, reescrevemos o código do método configStackView, levando em consideração as alterações feitas na interface do usuário.
MainViewController.swift func configStackView() -> UIStackView { let innerStackView: UIStackView = UIStackView(arrangedSubviews: [self.voteUpButton, self.voteDownButton]) // (N.1) innerStackView.axis = .horizontal // (N.2) innerStackView.distribution = .fillEqually let mainStackView: UIStackView = UIStackView(arrangedSubviews: [self.jokeLabel, innerStackView, self.chuckWhoButton]) // (N.3) mainStackView.spacing = 50 // (N.4) mainStackView.axis = .vertical mainStackView.distribution = .fillEqually self.view.addSubview(mainStackView) // (N.5) return mainStackView }
(N.1) Crie um innerStackView contendo dois botões para votação.
(N.2) Nós expomos o eixo vertical; a distribuição é a mesma.
(N.3) No mainStackView principal, adicione o innerStackView e o botão sobre Chuck Norris ao rótulo da última parte.
(N.4) Configure o mainStackView.
(N.5) Adicione mainStackView à hierarquia de visualizações.
Lançamos nosso aplicativo e vemos a interface como na Fig. 1
Fig. 1 A interface do nosso aplicativo com botões adicionais para votaçãoverificamos que ao votar a favor ou contra uma piada, as seguintes mensagens são exibidas no console:
345 bytes. Voted Up
ou
345 bytes. Voted Down
que indica uma solicitação POST concluída com êxito ao servidor.
Escrevemos um aplicativo de brincadeira usando a API do icndb e o verbo GET HTTP Fomos capazes de mostrar essas piadas no UIView e todas as piadas podem ser apreciadas. Essas ações enviam uma solicitação POST ao servidor remoto, o que deve salvar nossa estimativa.
Usando webViews para exibir páginas da web
Adicionaremos um webView para exibir a página da Wikipedia sobre Chuck Norris. Ela fica confusa com o toque de um botão.
No arquivo WebViewController.swift, escreva o seguinte stub da função:
WebViewController.swift import UIKit class WebViewController: UIViewController { lazy var webView: UIWebView = { // (O.1) self.configWebView() }() lazy var toolbar: UIToolbar = { // (O.2) self.configToolbar() }() override func viewDidLoad() { super.viewDidLoad() } func back() { // (O.3) self.webView.goBack() } func forward() { self.webView.goForward() } } extension WebViewController { func configWebView() -> UIWebView { // (O.4) } func configToolbar() -> UIToolbar { // (O.5) } func configConstraints() { // (O.6) } }
(O.1) Criamos variáveis preguiçosas para o webView
(O.2) e barra de ferramentas.
(O.3) Criamos métodos para frente e para trás, para navegação no histórico de visitas de links.
(O.4) Adicionamos métodos de inicialização do webView à extensão
(O.5) e toolBar
(O.6) e restrições para a interface.
Agora pintamos o código de cada função:
func configWebView() -> UIWebView { let webView: UIWebView = UIWebView()
(P.1) Inicializamos o webView e adicionamos o modo de exibição à hierarquia.
func configToolbar() -> UIToolbar { let toolbar: UIToolbar = UIToolbar(frame: CGRect.zero) // (Q.1) let backButton: UIBarButtonItem = UIBarButtonItem(title: , style: .plain, target: self, action: #selector(self.back)) // (Q.2) let forwardButton: UIBarButtonItem = UIBarButtonItem(title: , style: .plain, target: self, action: #selector(self.forward)) toolbar.setItems([backButton, forwardButton], animated: false) // (Q.3) toolbar.backgroundColor = UIColor.lightGray // (Q.4) self.view.addSubview(toolbar) return toolbar }
(Q.1) Crie uma barra de ferramentas com um quadro zero.
(Q.2) Criamos dois botões para navegar no histórico de links de visita. Com os nomes "<" e ">" e ações associadas ao método correspondente no webView.
(Q.3) Adicione esses botões à barra de ferramentas.
(Q.4) Defina a cor de fundo, como a barra de navegação, cinza claro. E adicione a visualização à hierarquia.
func configConstraints() { self.webView.translatesAutoresizingMaskIntoConstraints = false // (R.1) NSLayoutConstraint.activate([ // (R.2) self.webView.topAnchor.constraint(equalTo: self.topLayoutGuide.topAnchor), self.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.webView.bottomAnchor.constraint(equalTo: self.toolbar.topAnchor) ]) self.toolbar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ // (R.3) self.toolbar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.toolbar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.toolbar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), self.toolbar.heightAnchor.constraint(equalToConstant: 44.0) ]) }
(R.1) Desative as máscaras de redimensionamento automático para que não entrem em conflito com nossas restrições
(R.2) Para o webView, configure todos os quatro lados da exibição.
(R.3) Para a barra de ferramentas, configure três lados da vista, exceto a parte superior, mas defina adicionalmente a altura para o tamanho padrão 44.0.
E reescreva o código ViewDidLoad () da seguinte maneira:
override func viewDidLoad() { super.viewDidLoad() self.configConstraints()
(S.1) Invoque a configuração de restrições.
(S.2) Obtenha com segurança o URL na barra de endereços da página da Wikipedia
en.wikipedia.org/wiki/Chuck_Norris(S.3) Criamos uma solicitação a partir do URL.
(S.4) E executamos a solicitação fornecida no webView.
Retornamos ao MainViewController e o preenchemos com o método de chamar informações sobre Chuck Norris.
MainViewController.swift func chuckWho() { self.navigationController?.show(WebViewController(), sender: self)
(T.1) Ao clicar nesse botão na pilha navigationController, coloque uma nova instância do webView.
Fig. 2 demonstra como o webView fica em nosso aplicativo.
Fig. 2. A visão final do webView que implementamos