You have to override registerRoutableDestination
in router to register your module's class and its protocol. All routers' registerRoutableDestination
will be automatically called when app is launched.
You can create multi routers for the same class. For example, there can be multi routers for UIAlertController
, which providing different functions. Router A provides a compatible way to use UIAlertView
and UIAlertController
. Router B
just provides easy functions to use UIAlertController
. To use these routers in different situation, router A and B need to register different protocols, and get corresponding router with protocol.
class CommonAlertViewRouter: ZIKAnyViewRouter {
override class func registerRoutableDestination() {
registerView(UIAlertViewController.self)
registerView(UIAlertView.self)
register(RoutableView<CommonAlertViewInput>())
}
}
class EasyAlertViewRouter: ZIKAnyViewRouter {
override class func registerRoutableDestination() {
registerView(UIAlertViewController.self)
register(RoutableView<EasyAlertViewInput>())
}
}
The propose of registering destination class, is for error checking, and supporting storyboard. When a segue is performed, we need to search the UIViewController's router to config it.
When you create router and inject required dependencies for your own module, the user has to use this router and can't create another router. You can use +registerExclusiveView:
to make an exclusive registration. Then other router can't register this view, or there will be an assert failure.
class EditorViewRouter: ZIKAnyViewRouter {
override class func registerRoutableDestination() {
registerExclusiveView(EditorViewController.self)
}
}
Use common registration when the destination class is public, such as classes in system frameworks and third party. Use exclusiveness registration when the destination class is provided by your own.
You can register destination's protocol. Then you can get the router with the protocol, rather than import the router subclass.
And you can prepare the destination and do method injection with the protocol when performing route.
If your module is simple and all dependencies can be set on destination, you only need to use protocol conformed by destination.
If your module contains multi components, and those components' dependencies can't be passed through destination, you need a module config protocol, and configure components' dependencies inside router.
For example, when you pass a model to a VIPER module, the destination is the view in VIPER, and the view is not responsible for accepting any models.
///Module config protocol for editor module
protocol EditorModuleConfig {
var noteModel: Note?
}
///Use subclass of ZIKRouteConfiguration to save the custom config
class EditorModuleConfiguration: ZIKViewRouteConfiguration, EditorModuleConfig {
var noteModel: Note?
}
class EditorViewRouter: ZIKViewRouter<EditorViewController, EditorModuleConfiguration> {
override class func registerRoutableDestination() {
registerView(EditorViewController.self)
register(RoutableViewModule<EditorModuleConfig>())
}
//Use custom configuration
override defaultConfiguration() -> EditorModuleConfiguration {
return EditorModuleConfiguration()
}
override func destination(with configuration: EditorModuleConfiguration) -> EditorViewController? {
let sb = UIStoryboard.init(name: "Main", bundle: nil)
let destination = sb.instantiateViewController(withIdentifier: "EditorViewController") as! EditorViewController
return destination
}
override func prepareDestination(_ destination: EditorViewController, configuration: EditorModuleConfiguration) {
//Config VIPER module
let view = destination
guard view.presenter == nil else {
return
}
let presenter = EditorPresenter()
let interactor = EditorInteractor()
//Give model to interactor
interactor.note = configuration.noteModel
presenter.interactor = interactor
presenter.view = view
view.presenter = presenter
}
}
You can register a unique identifier for the router:
class EditorViewRouter: ZIKAnyViewRouter {
override class func registerRoutableDestination() {
registerIdentifier("viewController-editor")
}
}
Then you can get the router with the identifier:
var userInfo: [String : Any] = ... // Pass parameters in a dictionary
Router.to(viewIdentifier: "viewController-editor")?
.perform(path: .push(from: self), configuring: { (config, _) in
config.addUserInfo(userInfo)
})
We can't check type of parameters when passing in a dictionary, so it's not recommanded to use this method for most cases. You should only use this method when the module needs to support URL scheme. You can combine other URL router with ZIKRouter by identifier matching.
When app is launched, ZIKRouter will enumerate all classes and call router's registerRoutableDestination
method.
Enumerating all classes is optimized very fast, you don't need to worry about the performance. If your modules are more than 2000, you can try manually registration. The performance of auto registration is almost the same. The difference is you can register part of modules in groups, but not register all modules at same time.
Manually registration means calling each router's registerRoutableDestination
manually.
// Disable auto registration in +load, or before UIApplicationMain
+ (void)load {
ZIKRouteRegistry.autoRegister = NO;
[self registerForModulesBeforeRegistrationFinished];
}
+ (void)registerForModulesBeforeRegistrationFinished {
// Register routers used when registration is not finished yet.
}
But if some modules require some routers before you register them, then there will be assert failure, you should register those required routers earlier. Such as routable initial view controller from storyboard, or any routers used in this initial view controller.
Then you can register each router:
@import ZIKRouter.Internal;
#import "EditorViewRouter.h"
+ (void)registerRoutes {
[EditorViewRouter registerRoutableDestination];
...
// Finish
[ZIKRouteRegistry notifyRegistrationFinished];
}
You can use this functions to generate code for importing router headers and registering routers:
import ZIKRouter.Private
let importCode = codeForImportRouters()
let registeringCode = codeForRegisteringRouters()
@import ZIKRouter.Private;
NSString *importCode = codeForImportRouters();
NSString *registeringCode = codeForRegisteringRouters();
You may worry about the performance of registration, the next tests will resolve your doubt.
Here is the test of auto registration and manually registration:
- Test the time of 500, 1000, 2000, 5000 view controllers when auto registration and manually registration
- Register with
+registerExclusiveView:
and+registerViewProtocol:
ZIKRouter: register with blocks
means registering withZIKViewRoute
. It uses much less classes than using router subclasses, so it's a bit quicker, but it will cost more memorys.ZIKRouter: register with subclass
means registering with router subclassesZRouter: register with string
means declaring and registering routable pure swift protocols withinit(declaredTypeName:)
. The performance of ZRouter is slightly worse than ZIKRouter, bacause ZRouter needs to support both objc protocols and pure swift protocols.
There is no performance problem in 64 bit devices. In 32 bit devices like iPhone 5, most time is costed in objc method invocations. The time is almost the same even we replace registration methods with empty methods that do nothing.
If your project needs to support 32 bit device, and your modules are more than 1000, you can use manually registration. But you will not use manually registration in most of the time, because even in Wechat app, there are only about 700 hundred view controllers.
Here is the performance with other frameworks:
- Test the time of 500, 1000, 2000, 5000 view controllers when auto registration and manually registration
- Register with
+registerExclusiveView:
and+registerViewProtocol:
ZRouter: register with type
means declaring and registering routable pure swift protocols withinit(declaredProtocol:)
- Registering urls for other URL routers in a format like
/user/TestViewControllerxxx/:userID
Result:
- The best is
routable-ios
andJLRoutes
, than ZIKRouter and ZRouter - MGJRouter and HHRouter will processing the url string when registering, so there are a little performance costs.
- In Swift, the performances of
ZRouter: register with type
and Swinject are much worse than others, because they useString(describing:)
to convert swift type to string, andString(describing:)
has poor performance - Using
Zrouter: register with string
can easily and highly improve the performance, because it avoid usingString(describing:)
So, my conclusion is: you don't need to worry about the performance of registration.