ViperBase est une implémentation de l'architecture Viper pour l'utilisation dans la plate-forme iOS.
Ce projet vise à faciliter l'utilisation et l'adoption de la vipère , facilitant tous les paramètres nécessaires pour travailler avec cette architecture et essayer de réduire autant que possible le travail manuel s'est produit en raison de sa caractéristique.
Cocoapods est un gestionnaire de dépendances pour les projets de cacao. Pour intégrer ViperBase dans votre projet Xcode, spécifiez-le dans votre Podfile :
pod 'viper-base' , '~> 2.1' L'expérience la plus mauvaise que vous pouvez avoir lors de l'utilisation de Viper est de créer, vous-même , tous les fichiers, classes et protocoles nécessaires à cette architecture. Cela affecte beaucoup la productivité, ce qui fait une tâche simple prend beaucoup de temps.
Xcode nous permet de créer des modèles personnalisés. Avec cette ressource disponible, nous avons décidé de créer un modèle spécialement pour ViperBase . En utilisant ce modèle, il n'y aura plus de travail manuel.
Pour installer et utiliser notre modèle, consultez ce tutoriel.
Un module représente un écran de l'application.
Cette implémentation de Viper a des particularités:
Certaines approches considèrent la tâche de création du module comme une responsabilité du routeur . Mais cela viole le principe de responsabilité unique , car il est déjà responsable de la navigation entre les modules.
Pour résoudre ce problème, Builder a été créé. Il est chargé de créer tous les composants du module et d'effectuer les connexions respectives.
De cette façon, les entités peuvent être utilisées dans un ou plusieurs modules, car ce sont des structures simples sans logique commerciale.
Architecture iOS, la navigation est effectuée d'un UIViewController à un autre UIViewController . Pour cette raison, le routeur doit être propriétaire de l'opportunité de la vue du module actuel, uniquement à des fins de navigation , et de recevoir la vue du module de destination, à partir de son constructeur.
Les contrats définissent comment la communication entre les couches sera effectuée. Un module se compose de 5 contrats:
La classe de vue est conforme à ce protocole. Il définit la communication du présentateur à la vue
// MARK: - View Contract
protocol MyModuleViewProtocol : class {
} La classe de présentateur est conforme à ce protocole. Il définit la communication de la vue au présentateur
// MARK: - View Output Contract
protocol MyModuleViewOutputProtocol : class {
} La classe d'interacteur est conforme à ce protocole. Il définit la communication du présentateur à l'interacteur
// MARK: - Interactor Contract
protocol MyModuleInteractorProtocol : class {
} La classe de présentateur est conforme à ce protocole. Il définit la communication de l'interacteur à la présentatrice
// MARK: - Interactor Output Contract
protocol MyModuleInteractorOutputProtocol : class {
} La classe du routeur est conforme à ce protocole. Il définit la communication du présentateur à un routeur
// MARK: - Router Contract
protocol MyModuleRouterProtocol : class {
} final class MyModuleView : UIViewController , VIPERView {
var presenter : MyModuleViewOutputProtocol !
}
// MARK: - MyModuleViewProtocol
extension MyModuleView : MyModuleViewProtocol {
}Il vous suffit de mettre en œuvre les méthodes définies dans le contrat de vue .
final class MyModulePresenter : VIPERPresenter {
weak var view : MyModuleViewProtocol !
var interactor : MyModuleInteractorProtocol !
var router : MyModuleRouterProtocol !
}
// MARK: - MyModuleViewOutputProtocol
extension MyModulePresenter : MyModuleViewOutputProtocol {
}
// MARK: - MyModuleInteractorOutputProtocol
extension MyModulePresenter : MyModuleInteractorOutputProtocol {
}Il vous suffit de mettre en œuvre les méthodes définies dans le contrat de sortie de la vue et le contrat de sortie d'interacteur .
final class MyModuleInteractor : VIPERInteractor {
weak var presenter : MyModuleInteractorOutputProtocol !
}
// MARK: - MyModuleInteractorProtocol
extension MyModuleInteractor : MyModuleInteractorProtocol {
}Il vous suffit de mettre en œuvre les méthodes définies dans le contrat d'interacteur .
final class MyModuleRouter : VIPERRouter {
weak var viewController : UIViewController !
}
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
}Il vous suffit de mettre en œuvre les méthodes définies dans le contrat de routeur .
Dans le routeur, la navigation peut être effectuée de deux manières: présentant le module module ou poussant le module sur une pile de navigation . Pour effectuer la navigation, utilisez les méthodes ci-dessous:
- présentmodule (Withview: Embedin: Animated: Achèvement :)
Cette méthode présente le module suivant modalement. Vérifiez les détails des paramètres ci-dessous:
withView : View du module pour naviguer.embedIn : .navigationController ou .none . La valeur par défaut est .noneanimated : s'il faut effectuer ou non l'animation de la transition. La valeur par défaut est truecompletion : Handler appelé à la fin de la transition. La valeur par défaut est nil- PushModule (Withview: Embedin: Animated :)
Cette méthode pousse le module suivant sur la pile de navigation . Il ne fonctionne que si le module actuel est intégré dans un contrôleur de navigation ou fait partie d'une pile de navigation.
withView : View du module pour naviguer.embedIn : .navigationController ou .none . La valeur par défaut est .noneanimated : s'il faut effectuer ou non l'animation de la transition. La valeur par défaut est true Dans la classe Builder, vous spécifiez les classes respectives pour View , Presenter , Interactor et les couches router pour le module.
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 {
} Vous définissez également la façon dont l' interface utilisateur de la vue sera chargée , via la propriété defaultViewUIType . Il y a 3 valeurs possibles:
. storyboard ( name : " MyModuleView " , bundle : nil ) . nib ( name : " MyModuleView " , bundle : nil ) . noneLes 4 méthodes ci-dessous peuvent être utilisées pour construire un module. De plus, vous pouvez créer des méthodes de construction personnalisées , selon les besoins du module.
- construire() :
Crée le module et renvoie une structure VIPERModule contenant la view et les références presenter . Vous pouvez utiliser la référence du présentateur pour passer des données entre les modules .
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
func goToNextModule ( ) {
let module = NextModuleBuilder . build ( )
pushModule ( withView : module . view )
}
}- Build (Viewuitype :) :
Cette méthode fonctionne comme la méthode ci-dessus, mais il vous permet de spécifier le type d'interface utilisateur pendant l'appel de la méthode. Cette méthode est pratique lorsque vous utilisez typealias pour définir la configuration du générateur de modules.
typealias MyModuleBuilder = VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter >
MyModuleBuilder . build ( viewUIType : . storyboard ( name : " MyModuleView " , bundle : nil ) )Vous pouvez également utiliser cette méthode si vous avez l'intention d'effectuer des tests unitaires autour de la communication du module, en moquant une ou plusieurs classes de calques , selon les besoins de test.
import XCTest
class ProjectTests : XCTestCase {
//...
func testModuleCommunication ( ) {
typealias MockModuleBuilder = VIPERBuilder < ModuleMockView , ModuleOriginalPresenter , ModuleOriginalInteractor , ModuleOriginalRouter >
let module = MockModuleBuilder . build ( )
//...
}
} - [déprécié] BuildAndAttachTowIndow ()
Il s'agit d'une méthode de construction spéciale, généralement utilisée pour démarrer le module initial de l'application, appelée dans la 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
}
}
[MISE À JOUR]: Vous devez maintenant construire le module , en utilisant soit build() ou build(viewUIType:) Méthodes, puis appelez attachToWindow() ou attachToWindow(withNavigationController:) Nouvelles méthodes:
window = InitialModuleBuilder . build ( ) . attachToWindow ( ) window = InitialModuleBuilder . build ( ) . attachToWindow ( withNavigationController : true )[IMPORTANT]: Il est prévu de supprimer la méthode obsolète dans la prochaine version principale (v3.0)
- [déprécié] BuildAndAttachTonaVigationController (Tabbaritem :)
Cette méthode crée le module, la joindre à un contrôleur de navigation et renvoie la référence du contrôleur de navigation . Si vous avez l'intention d'utiliser le module à l'intérieur d'un contrôleur de barre d'onglet , vous pouvez utiliser le paramètre tabBarItem pour configurer l'élément de barre d'onglet pour ce module.
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)
]
[MISE À JOUR]: Vous devez maintenant construire le module , en utilisant soit build() ou build(viewUIType:) Méthodes, puis appelez attachToNavigationController() Nouvelle méthode:
window = MyModuleBuilder . build ( ) . attachToNavigationController ( )[IMPORTANT]: Il est prévu de supprimer la méthode obsolète dans la prochaine version principale (v3.0)
Si vous avez un module spécifique qui prévoit de recevoir certaines données , il est pratique de créer une méthode de construction personnalisée pour ce module. De cette façon, le constructeur sera chargé de passer ces données au presenter .
Pour créer des méthodes de construction personnalisées:
build() ;presenter , selon la mise en œuvre;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
}
}Les routeurs peuvent appeler ce constructeur comme ceci:
let view = NextModuleBuilder . build ( someData : " Example of data " , anotherData : " Another example of data " ) pushModule ( withView : view ) presentModule ( withView : view ) Viperbase est libéré sous la licence MIT.