O ViperBase é uma implementação da arquitetura Viper para uso na plataforma iOS.
Este projeto tem como objetivo facilitar o uso e a adoção do uso do Viper , facilitando todas as configurações necessárias para trabalhar com essa arquitetura e tentar reduzir o máximo possível, o trabalho manual surgido devido à sua característica.
O Cocoapods é um gerente de dependência de projetos de cacau. Para integrar o ViperBase ao seu projeto Xcode, especifique -o no seu Podfile :
pod 'viper-base' , '~> 2.1' A experiência mais ruim que você pode ter ao usar o Viper é criar, por si mesmo , todos os arquivos, classes e protocolos necessários para essa arquitetura. Isso afeta muito a produtividade, tornando uma tarefa simples levar muito tempo.
O Xcode nos permite criar modelos personalizados. Com este recurso disponível, decidimos criar um modelo especialmente para o ViperBase . Usando este modelo, não haverá mais trabalho manual.
Para instalar e usar nosso modelo, verifique este tutorial.
Um módulo representa uma tela do aplicativo.
Esta implementação do Viper tem algumas particularidades:
Algumas abordagens consideram a tarefa de criação do módulo como uma responsabilidade do roteador . Mas isso viola o princípio de responsabilidade única , pois já é responsável pela navegação entre os módulos.
Para resolver esse problema, o construtor foi criado. É responsável por criar todos os componentes do módulo e fazer as respectivas conexões.
Dessa forma, as entidades podem ser usadas em um ou mais módulos, pois são estruturas simples sem lógica de negócios.
Arquitetura iOS, a navegação é realizada de um UIViewController para outro uiviewController . Por esse motivo, o roteador deve possuir a referência para a visão do módulo atual, apenas para fins de navegação , e receber a visão do módulo de destino, de seu construtor.
Os contratos definem como a comunicação entre as camadas será feita. Um módulo consiste em 5 contratos:
A classe View está em conformidade com este protocolo. Ele define a comunicação do apresentador para visualizar
// MARK: - View Contract
protocol MyModuleViewProtocol : class {
} A classe do apresentador está em conformidade com este protocolo. Ele define a comunicação de vista para o apresentador
// MARK: - View Output Contract
protocol MyModuleViewOutputProtocol : class {
} A classe Inteattor está em conformidade com este protocolo. Ele define a comunicação do apresentador para o interator
// MARK: - Interactor Contract
protocol MyModuleInteractorProtocol : class {
} A classe do apresentador está em conformidade com este protocolo. Ele define a comunicação do interator ao apresentador
// MARK: - Interactor Output Contract
protocol MyModuleInteractorOutputProtocol : class {
} A classe do roteador está em conformidade com este protocolo. Ele define a comunicação do apresentador para o roteador
// MARK: - Router Contract
protocol MyModuleRouterProtocol : class {
} final class MyModuleView : UIViewController , VIPERView {
var presenter : MyModuleViewOutputProtocol !
}
// MARK: - MyModuleViewProtocol
extension MyModuleView : MyModuleViewProtocol {
}Você só precisa implementar os métodos definidos no contrato de exibição .
final class MyModulePresenter : VIPERPresenter {
weak var view : MyModuleViewProtocol !
var interactor : MyModuleInteractorProtocol !
var router : MyModuleRouterProtocol !
}
// MARK: - MyModuleViewOutputProtocol
extension MyModulePresenter : MyModuleViewOutputProtocol {
}
// MARK: - MyModuleInteractorOutputProtocol
extension MyModulePresenter : MyModuleInteractorOutputProtocol {
}Você só precisa implementar os métodos definidos no contrato de saída de exibição e no contrato de saída do interator .
final class MyModuleInteractor : VIPERInteractor {
weak var presenter : MyModuleInteractorOutputProtocol !
}
// MARK: - MyModuleInteractorProtocol
extension MyModuleInteractor : MyModuleInteractorProtocol {
}Você só precisa implementar os métodos definidos no contrato interator .
final class MyModuleRouter : VIPERRouter {
weak var viewController : UIViewController !
}
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
}Você só precisa implementar os métodos definidos no contrato do roteador .
No roteador, a navegação pode ser feita de duas maneiras: apresentar o módulo modalmente ou empurrar o módulo para uma pilha de navegação . Para executar a navegação, use os métodos abaixo:
- PresentModule (WithView: InckEdIn: animado: conclusão :)
Este método apresenta o próximo módulo modalmente. Verifique os parâmetros Detalhes abaixo:
withView : View do módulo para navegar.embedIn : .navigationController ou .none . O valor padrão é .noneanimated : se deve ou não executar a animação da transição. O valor padrão é truecompletion : o manipulador chamado quando a transição termina. O valor padrão é nil- PushModule (WithView: INCEDIN: animado :)
Este método empurra o próximo módulo para a pilha de navegação . Ele só funciona se o módulo atual for incorporado em um controlador de navegação ou faz parte de uma pilha de navegação.
withView : View do módulo para navegar.embedIn : .navigationController ou .none . O valor padrão é .noneanimated : se deve ou não executar a animação da transição. O valor padrão é true Na classe Builder, você especifica as respectivas classes para View , Presenter , Interactor e camadas router para o módulo.
final class MyModuleBuilder : VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter > {
override class var defaultViewUIType : VIPERViewUIType {
return . storyboard ( name : " MyModuleView " , bundle : nil )
}
}
// MARK: - Builder custom methods
extension MyModuleBuilder {
} Você também define a maneira como a interface do usuário do View será carregada , através da propriedade defaultViewUIType . Existem 3 valores possíveis:
. storyboard ( name : " MyModuleView " , bundle : nil ) . nib ( name : " MyModuleView " , bundle : nil ) . noneOs 4 métodos abaixo podem ser usados para construir um módulo. Além disso, você pode criar métodos de construção personalizados , de acordo com as necessidades do módulo.
- construir() :
Cria o módulo e retorna uma estrutura VIPERModule que contém as referências de view e presenter . Você pode usar a referência do apresentador para transmitir dados entre os módulos .
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
func goToNextModule ( ) {
let module = NextModuleBuilder . build ( )
pushModule ( withView : module . view )
}
}- Build (Viewitype :) :
Este método funciona como o método acima, mas permite especificar o tipo de interface do usuário durante a chamada do método. Este método é conveniente quando você está usando typealias para definir a configuração do construtor de módulos.
typealias MyModuleBuilder = VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter >
MyModuleBuilder . build ( viewUIType : . storyboard ( name : " MyModuleView " , bundle : nil ) )Você também pode usar esse método se pretender realizar testes de unidade em torno da comunicação do módulo, zombando de uma ou mais classes de camada , de acordo com as necessidades de teste.
import XCTest
class ProjectTests : XCTestCase {
//...
func testModuleCommunication ( ) {
typealias MockModuleBuilder = VIPERBuilder < ModuleMockView , ModuleOriginalPresenter , ModuleOriginalInteractor , ModuleOriginalRouter >
let module = MockModuleBuilder . build ( )
//...
}
} - [depreciado] BuildAndattachTowindow ()
Este é um método de construção especial, geralmente usado para iniciar o módulo inicial do aplicativo, chamado na classe AppDelegate :
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = InitialModuleBuilder.buildAndAttachToWindow()
return true
}
}
[Atualizado]: Agora você precisa criar o módulo primeiro , usando build() ou build(viewUIType:) Métodos, depois ligue para attachToWindow() ou attachToWindow(withNavigationController:) Novos métodos:
window = InitialModuleBuilder . build ( ) . attachToWindow ( ) window = InitialModuleBuilder . build ( ) . attachToWindow ( withNavigationController : true )[IMPORTANTE]: está planejado para remover o método depreciado na próxima liberação principal (v3.0)
- [depreciado] BuildAndattachtonAvigationController (TabBariTem :)
Este método cria o módulo, conecte -o a um controlador de navegação e retorna a referência do controlador de navegação . Se você pretende usar o módulo dentro de um controlador de barra de guia , poderá usar o parâmetro tabBarItem para configurar o item da barra da guia para este módulo.
let tabBarController = UITabBarController()
let bookmarksItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 0)
let contactsItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let downloadsItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 2)
tabBarController.viewControllers = [
BookmarksBuilder.buildAndAttachToNavigationController(tabBarItem: bookmarksItem),
ContactsBuilder.buildAndAttachToNavigationController(tabBarItem: contactsItem),
DownloadsBuilder.buildAndAttachToNavigationController(tabBarItem: downloadsItem)
]
[Atualizado]: Agora você precisa criar o módulo primeiro , usando build() ou build(viewUIType:) , depois ligue para attachToNavigationController() Novo Método:
window = MyModuleBuilder . build ( ) . attachToNavigationController ( )[IMPORTANTE]: está planejado para remover o método depreciado na próxima liberação principal (v3.0)
Se você possui um módulo específico que espera receber alguns dados , é conveniente criar um método de construção personalizado para esse módulo. Dessa forma, o construtor será responsável por passar esses dados para o presenter .
Para criar métodos de construção personalizados:
build() ;presenter , de acordo com a implementação;view . // MARK: - Builder custom methods
extension NextModuleBuilder {
static func build ( someData : Any , anotherData : Any ) -> MyModuleView {
let module = build ( )
module . presenter . someData = someData
module . presenter . anotherData = anotherData
return module . view
}
}Os roteadores podem chamar esse construtor assim:
let view = NextModuleBuilder . build ( someData : " Example of data " , anotherData : " Another example of data " ) pushModule ( withView : view ) presentModule ( withView : view ) O ViperBase é liberado sob a licença do MIT.