NOTE: Base code is a fork of Rambler VIPER McFlurry
VIPER McFlurry is a modern framework for implementing VIPER architecture in iOS application. It offers several tools and components that help either start new projects with VIPER or move from MVC.
See CHANGELOG
- The framework itself pushes you to implement a proper VIPER architecture,
- It provides an extremely nice and easy way to implement intermodule data transfer,
- Used in default Generamba template.
This example works only for Module with
UIViewController
as View. However, it's possible to use this approach even withUIView
andUITableViewCell
acting as View.
- Create Module input protocol for target module inherited from
RamblerViperModuleInput
:
@protocol SomeModuleInput <RamblerViperModuleInput>
- (void)moduleConfigurationMethod;
@end
- Make Presenter of target module conform to this protocol.
- Inject Presenter as moduleInput property of the view. You can skip this step if presenter is a view property with name "output".
- Add Segue from source module ViewController to target module ViewController.
- Inject Source ViewController into Source Router as property "transition handler".
- In Router method call transition handler to open target module with configuration during segue.
[[self.transitionHandler openModuleUsingSegue:SegueIdentifier]
thenChainUsingBlock:^id<RamblerViperModuleOutput>(id<SomeModuleInput> moduleInput) {
[moduleInput moduleConfigurationMethod];
return nil;
}];
- Create Module output protocol for target module inherited from
RamblerViperModuleOutput
:
@protocol SomeModuleOutput <RamblerViperModuleOutput>
- (void)moduleConfigurationMethod;
@end
- Make source module presenter to conform to this protocol.
- Add to target module presenter method:
- (void)setModuleOutput:(id<RamblerViperModuleOutput>)moduleOutput;
- Return source module presenter from configuration block in router:
[[self.transitionHandler openModuleUsingSegue:SegueIdentifier]
thenChainUsingBlock:^id<RamblerViperModuleOutput>(id<SomeModuleInput> moduleInput) {
[moduleInput moduleConfigurationMethod];
return sourceRouterPresenter; // Return of module output
}];
Module factory can be replaced with segues for most cases. Except you need to create complex module or nontrivial module instantiation logic.
- Use
RamblerViperModuleFactory
object as module fabric with Typhoon. - Set definition Initializer to
initWithViewControllerLoader:andViewControllerIdentifier:
- First parameter is the object which loads the view controller, e.g. UIStoryboard or TyphoonNibLoader instance,
- Second parameter is view controller's identifier, e.g. RestorationID or NibName of ViewController.
- Typhoon will initialize module from ViewController.
- Inject this Factory into router.
- Call Transition Handler's method
- openModuleUsingFactory:withTransitionBlock:
. - Second block is place where transition from one to another viewController/transitionHandler should be performed:
[[self.transitionHandler openModuleUsingFactory:self.betaModuleFactory
withTransitionBlock:^(id <RamblerViperModuleTransitionHandlerProtocol> sourceModuleTransitionHandler,
id <RamblerViperModuleTransitionHandlerProtocol> destinationModuleTransitionHandler) {
UIViewController *sourceViewController = (id) sourceModuleTransitionHandler;
UIViewController *destinationViewController = (id) destinationModuleTransitionHandler;
[sourceViewController.navigationController pushViewController:destinationViewController
animated:YES];
}] thenChainUsingBlock:^id<RamblerViperModuleOutput>(id<RamblerModuleBetaInput> moduleInput) {
[moduleInput configureWithExampleString:exampleString];
return nil;
}];
import ViperMcFlurryX
transitionHandler.openModule!(usingFactory: SomeModuleConfigurator()) { (sourceModuleTransitionHandler, destinationModuleTransitionHandler) in
let sourceViewController = sourceModuleTransitionHandler as! UIViewController
let destinationViewController = destinationModuleTransitionHandler as! UIViewController
sourceViewController.present(destinationViewController, animated: true, completion: nil)
}.thenChain { (moduleInput) -> ViperModuleOutput? in
(moduleInput as! SomeModuleInput).configure()
return nil // or self.calleeOutput
}
import ViperMcFlurryX_Swift
transitionHandler.openModuleUsingFactory(SomeModuleConfigurator()) { (sourceModuleTransitionHandler, destinationModuleTransitionHandler) in
let sourceViewController = sourceModuleTransitionHandler as! UIViewController
let destinationViewController = destinationModuleTransitionHandler as! UIViewController
sourceViewController.present(destinationViewController, animated: true, completion: nil)
}.thenChainUsingBlock { (moduleInput) -> ViperModuleOutput? in
(moduleInput as! SomeModuleInput).configure()
return nil // or self.calleeOutput
}
// will use ViperModuleViewControllerPresenter
transitionHandler.openModuleUsingFactory(SomeModuleConfigurator()).thenChainUsingBlock { (moduleInput) -> ViperModuleOutput? in
(moduleInput as! SomeModuleInput).configure()
return nil // or self.calleeOutput
}
- In example above one module is pushed to navigation stack of another module.
- Modules are linked with intermodule data transfer block.
For example, you have present module A, then present module B from A. Your task requires dismiss B and A at the same time directly to parent of A.
In this case you can set self.transitionHandler.skipOnDismiss = true
of A module, as result, when B call closeCurrentModule
, then
B and A will be dismissed together, and presenter of B will be notified via moduleDidSkipOnDismiss
(if implemented).
Add to podfile:
pod "ViperMcFlurryX"
For swift version:
pod "ViperMcFlurryX_Swift"
MIT
ViperMcFlurryX:
- Siarhei Ladzeika / [email protected]
- Cheslau Bachko / https://github.com/CheslauBachko
Original code from Rambler&Co team:
- Andrey Zarembo-Godzyatsky / [email protected]
- Valery Popov / [email protected]
- Egor Tolstoy / [email protected]