Bridge Components

Vamos avançar na transformação da aplicação para torná-la mais nativa. Uma abordagem eficaz é utilizar Bridge Components (antigamente conhecidos como Strada Components), que criam uma integração entre a aplicação web e iOS, adicionando elementos nativos.

Primeiro Bridge Component

No capítulo anterior, identificamos o botão New URL na tela de URLs. Vamos substituí-lo por um botão nativo, removendo-o da interface web para uma aparência mais integrada e nativa.

Passo a passo para criar o botão New URL na sua versão nativa usando um Bridge Component:

  1. Crie um Bridge Component no projeto iOS que receba as instruções do Rails.
  2. Crie um Stimulus Controller que, ao disparar o evento connect, envie as informações necessárias para renderizar o elemento nativo.
  3. Configure o markup na página onde o componente será exibido.

iOS Bridge Button Component

No projeto iOS, clique com o botão direito na árvore de arquivos e selecione New Empty File ou use o atalho Command + N. Crie um arquivo Swift chamado ButtonComponent. O código a seguir contém comentários para facilitar o entendimento de quem não tem experiência prévia com Swift.

Implementação do componente Swift

import HotwireNative
import UIKit

final class ButtonComponent: BridgeComponent {

  // Nomeia o componente para permitir a conexão
  // com o Stimulus Controller por meio desta string.
  override class var name: String { "button" }

  // No evento `connect` do Stimulus, o componente recebe
  // informações necessárias, como o título do botão.
  override func onReceive(message: Message) {

      // Use print(message) para verificar os dados recebidos do Rails.
      // O conteúdo será exibido nos logs do Xcode.
      guard let viewController else { return }
      addButton(via: message, to: viewController)
  }

  private var viewController: UIViewController? {
    delegate.destination as? UIViewController    
}

  // Função responsável por montar e renderizar o botão nativo
  // na tela do app iOS e adicioná-lo à Navbar no lado direito
  // em resposta ao evento `connect` do Stimulus Controller.
  private func addButton(via message: Message, to viewController: UIViewController) {

    guard let data: MessageData = message.data() else { return }

    let action = UIAction { [unowned self] _ in
      self.reply(to: "connect")
    }

    let item = UIBarButtonItem(title: data.title, primaryAction: action)
    viewController.navigationItem.rightBarButtonItem = item
  }
}

// Decodifica a mensagem do Stimulus Controller
// para a struct `MessageData`. Classes, structs e extensions
// foram discutidos na seção sobre Swift.
private extension ButtonComponent {
  struct MessageData: Decodable {
    let title: String
  }
}

Registre o componente no Hotwire Native para torná-lo disponível no projeto e acessível conforme necessário. No arquivo AppDelegate, insira o seguinte código abaixo da configuração do PathConfiguration:

Hotwire.registerBridgeComponents([
  ButtonComponent.self
])

Conectando o Rails ao componente nativo

Certifique-se de que os pacotes necessários estejam instalados no projeto Rails:

./bin/importmap pin @hotwired/stimulus @hotwired/hotwire-native-bridge

Para concluir a integração e habilitar o Rails a chamar o componente nativo, crie um novo Stimulus Controller no caminho e nome indicados:

app/javascript/controllers/button_controller.js

Implementação do controller Stimulus no Rails

import { BridgeComponent } from "@hotwired/hotwire-native-bridge"

export default class extends BridgeComponent {
  // Lembra da string no Component Swift?
  static component = "button"

  // O método `connect` é executado quando o elemento está pronto,
  // informando à bridge o componente chamado e definindo a ação
  // do botão nativo via callback.
  connect() {
    super.connect()

    const element = this.bridgeElement
    const title = element.bridgeAttribute("title")
    this.send("connect", {title}, () => {
      this.element.click()
    })  
  }
}

Conectando o botão New URL ao controller Stimulus

Para concluir, é necessário disparar o método connect do Stimulus Controller. Isso é feito através de um elemento HTML. Atualize o botão New URL no arquivo:

app/views/urls/index.html.erb

No helper link_to, adicione os atributos data necessários para que o Stimulus Controller seja acionado, incluindo o nome do controller e o título do botão:

<%= link_to "Back to urls", urls_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium", 
data: { controller: "button", bridge_title: "New Url" } %>

Após a atualização, execute novamente o aplicativo iOS (Command + R). Navegue até URLs e observe que o botão nativo aparece conforme implementado. Quando clicado, ele executa a mesma ação que o botão web. Isso ocorre porque o Stimulus Controller, por meio do callback no método connect e na chamada this.send, instrui que o evento acionado ao clicar no botão nativo seja equivalente ao clique no botão web (this.element.click()).

Screenshot 2024-12-22 at 11.41.15.png

Recapitulando

Para compreender os Bridge Components, é essencial considerar três elementos principais:

  1. Swift Component: Código nativo responsável por implementar a funcionalidade no iOS (ou Android).
  2. Stimulus Controller: Código JavaScript que conecta-se a um elemento HTML, envia mensagens à bridge nativa para executar ou exibir funcionalidades, transmite os dados necessários para montar o elemento nativo e define o callback.
  3. Markup HTML (ERB): Estrutura HTML que chama o Stimulus Controller, passando os atributos data-bridge necessários para o funcionamento do componente nativo.

Esses passos representam, de forma simplificada, o fluxo de integração utilizando a bridge nativa.

Screenshot 2024-12-22 at 11.52.44.png