Olá leitor!
Em um artigo
anterior , falei sobre o ciclo
VIP da arquitetura
Clean Swift . Agora, abordaremos um dos tópicos mais importantes - a
transição e a
transferência de dados entre cenas.

Teoria
O componente
Router é responsável pela lógica de navegação e transferência de dados, que faz parte da cena (opcional, é claro). É inicializado no
ViewController , junto com o
Interactor e o
Presenter .
O roteador implementa dois protocolos -
RoutingLogic e
DataPassing , que preencheremos com nossa funcionalidade.
RoutingLogic deve conter os métodos responsáveis pela transição para uma cena específica.
DataPassing contém a variável
dataStore , que se refere ao Protocolo
DataStore .
O Interactor Scenes implementa o protocolo
DataStore e trabalha com as variáveis armazenadas nele.
O próprio
roteador contém um link para o
ViewController de sua cena.
Usando o link para o
ViewController , o
roteador salta entre as cenas. Para fazer isso, você pode usar o
Segue ou criar uma cena para a qual deseja fazer uma transição programaticamente. Qual método usado não é importante, o principal para nós é ter um link para uma instância da classe
ViewController para a qual estamos mudando.
Usando o link para o
DataStore, transferiremos dados do
Interator de uma cena para o
Interactor da cena para a qual estamos mudando. E, como mencionado anteriormente, é o
roteador que precisa saber como fazer isso.
Prática
Por exemplo, transferiremos o texto do TextField para o Label de outra cena. Vamos considerar duas maneiras de transição entre cenas - de acordo com
Segue e programaticamente.
A classe
Router contém 3 grupos de métodos semânticos:
- Métodos da implementação RoutingLogic (routeTo)
- Métodos responsáveis pela navegação (navigateTo, transição sem Segue)
- Métodos para transferência de dados (passDataTo, se houver dados para transferência)

Se fizermos uma transição pelo
Segue , por exemplo, quando um botão é clicado, no
ViewController , devemos substituir o método prepare (for: sender :). Esta extensão permitirá que você chame automaticamente métodos do
roteador com o nome de
Segue .
A substituição da preparação (para: remetente :) é opcional ao trabalhar com o Segue. Você pode excluí-lo do código e chamar performSegue (withIdentifier: sender :) no método Router . A preparação é necessária apenas se você precisar usar o Segue junto com a transferência de dados. | final class HomeViewController: UIViewController { |
| // ... |
| |
| override func prepare(for segue: UIStoryboardSegue, sender: Any?) { |
| // Segue |
| if let scene = segue.identifier { |
| |
| // , Router |
| // router?.routeToNAME(segue:) |
| let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") |
| |
| // , |
| // Segue |
| if let router = router, router.responds(to: selector) { |
| router.perform(selector, with: segue) |
| } |
| } |
| } |
| |
| // ... |
| } |
Agora finalmente chegamos à coisa mais interessante - o código do
roteador . O exemplo contém comentários, portanto, consideraremos apenas os principais pontos.
Neste
roteador, trabalhamos com duas cenas -
Casa e
Detalhe . A transição da cena
inicial também é tratada de duas maneiras - por
Segue e
programaticamente . Os dados são transferidos da cena
inicial para a cena
detalhada .
Todos os métodos no protocolo
RoutingLogic devem ser nomeados de acordo com o princípio
routeToNAME , em que
NAME é o nome do
Segue (Identificador) que especificamos ao trabalhar com o
Storyboard . Isso é necessário não apenas para a conveniência e a beleza do uso, mas também para o nosso seletor de métodos em
prepare (para: remetente :) ViewController , que redefinimos anteriormente.
Também na classe
HomeRouter, existem métodos que começam com
navigateTo e
passDataTo . Os primeiros são responsáveis pela lógica de transição, enquanto os últimos são responsáveis pela transferência de dados. Os métodos navigateTo são criados apenas se a transição for feita programaticamente.
No exemplo, temos um
método routeToDetail (segue :) . O parâmetro
segue é opcional, pois o método contém uma implementação que permite chamá-lo sem usar o
Segue . Nos dois casos de transição, obtemos valores não opcionais da cena
HomeViewController'a e
HomeDataStore'a Home , bem como links para a
cena ViewController e
DataStore Detail . Aqui vale a pena prestar atenção ao fato de que
detailDS é uma variável e é passada para o método
passDataToDetail usando o parâmetro
pass- through (inout). Isso é importante porque sem o
inout, teremos de marcar todos os protocolos do
DataStore como “possíveis de implementar apenas com classes” (protocolo DetailDataStore: class), e isso implica muitas dificuldades, incluindo a captura de links fortes.
| |
| import UIKit |
| |
| /// @objc |
| /// prepare View Controller'e |
| @objc protocol HomeRoutingLogic { |
| /// Detail View Controller |
| func routeToDetail(segue: UIStoryboardSegue?) |
| } |
| |
| protocol HomeDataPassing { |
| var dataStore: HomeDataStore? { get } |
| } |
| |
| final class HomeRouter: NSObject, HomeRoutingLogic, HomeDataPassing { |
| |
| // MARK: - Private |
| |
| // MARK: - Public |
| |
| weak var viewController: HomeViewController? |
| var dataStore: HomeDataStore? |
| |
| // MARK: - HomeRoutingLogic |
| |
| func routeToDetail(segue: UIStoryboardSegue?) { |
| if let segue = segue { |
| // |
| // Segue |
| |
| // Detail View Controller |
| // Data Store Router'e |
| guard |
| let homeDS = dataStore, |
| let detailVC = segue.destination as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| // , , , |
| // "" |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| } else { |
| // , |
| // Segue |
| |
| // Detail View Controller Storyboard'a |
| // Data Store Router'e |
| guard |
| let viewController = viewController, |
| let homeDS = dataStore, |
| let storyboard = viewController.storyboard, |
| let detailVC = storyboard.instantiateViewController(withIdentifier: "Detail") as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| |
| // , "" |
| navigateToDetail(source: viewController, destination: detailVC) |
| } |
| } |
| |
| // MARK: - Navigation |
| |
| private func navigateToDetail(source: HomeViewController, destination: DetailViewController) { |
| source.navigationController?.pushViewController(destination, animated: true) |
| } |
| |
| // MARK: - Passing data |
| |
| /// destination inout, |
| /// Data Store |
| private func passDataToDetail(source: HomeDataStore, destination: inout DetailDataStore) { |
| |
| // HomeDataStore DetailDataStore |
| destination.message = source.message |
| } |
| } |
Conclusão
Só isso. Obrigado por ler até o fim! Abaixo deixarei um link para o projeto, se você quiser experimentar o artigo em ação.
Série de artigos
- Visão geral da arquitetura Clean Swift
- Roteador e transmissão de dados na arquitetura Clean Swift (você está aqui)
- Trabalhadores da arquitetura Clean Swift
- Teste de unidade na arquitetura Clean Swift
- Um exemplo de uma arquitetura simples de loja online Clean Swift
Link para o projetoAjuda para escrever um artigo:
Bastien