{"id":778,"url":"https://github.com/Zuikyo/ZIKRouter","last_synced_at":"2025-07-30T19:32:09.080Z","repository":{"id":56930602,"uuid":"97612413","full_name":"Zuikyo/ZIKRouter","owner":"Zuikyo","description":"Interface-oriented router for discovering modules, and injecting dependencies with protocol in Objective-C and Swift.","archived":false,"fork":false,"pushed_at":"2019-08-28T11:51:25.000Z","size":4682,"stargazers_count":659,"open_issues_count":4,"forks_count":115,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-11-14T13:11:46.860Z","etag":null,"topics":["dependency-injection","interface-oriented","ios","modular","protocol-oriented","router","swift"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Zuikyo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-18T15:04:24.000Z","updated_at":"2024-07-24T08:47:49.000Z","dependencies_parsed_at":"2022-08-21T00:01:13.905Z","dependency_job_id":null,"html_url":"https://github.com/Zuikyo/ZIKRouter","commit_stats":null,"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zuikyo%2FZIKRouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zuikyo%2FZIKRouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zuikyo%2FZIKRouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Zuikyo%2FZIKRouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Zuikyo","download_url":"https://codeload.github.com/Zuikyo/ZIKRouter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228178882,"owners_count":17881104,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","interface-oriented","ios","modular","protocol-oriented","router","swift"],"created_at":"2024-01-05T20:15:31.115Z","updated_at":"2024-12-04T19:31:54.937Z","avatar_url":"https://github.com/Zuikyo.png","language":"Objective-C","funding_links":[],"categories":["App Routing","Foundation"],"sub_categories":["Getting Started"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"Documentation/Resources/icon.png\" width=\"33%\"\u003e\n\u003c/p\u003e\n\n# ZIKRouter\n\n![](https://img.shields.io/cocoapods/p/ZIKRouter.svg?style=flat)\n![](https://img.shields.io/badge/language-objectivec-blue.svg)\n![](https://img.shields.io/badge/language-swift-orange.svg)\n![ZIKRouter](https://img.shields.io/cocoapods/v/ZIKRouter.svg?style=flat)\n![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)\n![license](https://img.shields.io/github/license/mashape/apistatus.svg)\n\nAn interface-oriented router for managing modules and injecting dependencies with protocol.\n\nThe view router can perform all navigation types in UIKit / AppKit through one method.\n\nThe service router can discover and prepare corresponding module with its protocol.\n\n---\n\n一个用于模块间解耦和通信，基于接口进行模块管理和依赖注入的组件化路由工具。用多种方式最大程度地发挥编译检查的功能。\n\n通过 protocol 寻找对应的模块，并用 protocol 进行依赖注入和模块通信。\n\nService Router 可以管理任意自定义模块。View Router 进一步封装了界面跳转。\n\n### [中文文档](Documentation/Chinese/README.md)\n\n---\n\n## Features\n\n- [x] Swift and Objective-C support\n- [x] iOS, macOS and tvOS support\n- [x] File template for quickly creating router\n- [x] Routing for UIViewController / NSViewController, UIView / NSView and any class\n- [x] Dependency injection, including dynamic injection and static injection\n- [x] **Declaration of routable protocol for compile-time checking. Using undeclared protocol will bring compiler error. This is one of the most powerful feature**\n- [x] **Module matching with its protocol**\n- [x] **URL routing support**\n- [x] **Configure the module with its protocol rather than a parameter dictionary**\n- [x] **Required protocol and provided protocol for making thorough decouple**\n- [x] **Adapter for decoupling modules and add compatible interfaces**\n- [x] **Storyboard support. Views from a segue can be auto prepared**\n- [x] Encapsulation for all transition methods and unwind methods in UIKit / AppKit, and also custom transition\n- [x] Error checking for view transition\n- [x] AOP for view transition\n- [x] Memory leak detection\n- [x] Custom events handling\n- [x] Auto registration\n- [x] Highly scalable\n\n## Quick Start Guide\n\n1. [Create Router](#1-Create-Router)\n   1. [Router Subclass](#11-Router-Subclass)\n   2. [Simple Router](#12-Simple-Router)\n2. [Declare Routable Type](#2-Declare-Routable-Type)\n3. [View Router](#View-Router)\n   1. [Transition directly](#Transition-directly)\n   2. [Prepare before Transition](#Prepare-before-Transition)\n   3. [Make Destination](#Make-Destination)\n   4. [Required Parameter and Special Parameter](#Required-Parameter-and-Special-Parameter)\n   5. [Perform on Destination](#Perform-on-Destination)\n   6. [Prepare on Destination](#Prepare-on-Destination)\n   7. [Remove](#Remove)\n   8. [Adapter](#Adapter)\n   9. [Modularization](#Modularization)\n   10. [URL Router](#URL-Router)\n   11. [Other Features](#Other-Features)\n4. [Service Router](#Service-Router)\n5. [Demo and Practice](#Demo-and-Practice)\n6. [File Template](#File-Template)\n\n## Documentation\n\n### Design Idea\n\n[Design Idea](Documentation/English/DesignPhilosophy.md)\n\n### Basics\n\n1. [Router Implementation](Documentation/English/RouterImplementation.md)\n2. [Module Registration](Documentation/English/ModuleRegistration.md)\n3. [Routable Declaration](Documentation/English/RoutableDeclaration.md)\n4. [Type Checking](Documentation/English/TypeChecking.md)\n5. [Perform Route](Documentation/English/PerformRoute.md)\n6. [Remove Route](Documentation/English/RemoveRoute.md)\n7. [Transfer Parameters with Custom Configuration](Documentation/English/CustomConfiguration.md)\n\n### Advanced Features\n\n1. [Error Handle](Documentation/English/ErrorHandle.md)\n2. [Storyboard and Auto Create](Documentation/English/Storyboard.md)\n3. [AOP](Documentation/English/AOP.md)\n4. [Dependency Injection](Documentation/English/DependencyInjection.md)\n5. [Circular Dependency](Documentation/English/CircularDependencies.md)\n6. [Module Adapter](Documentation/English/ModuleAdapter.md)\n7. [Unit Test](Documentation/English/UnitTest.md)\n\n[FAQ](Documentation/English/FAQ.md)\n\n## Requirements\n\n* iOS 7.0+\n* Swift 3.2+\n* Xcode 9.0+\n\n## Installation\n\n### Cocoapods\n\nAdd this to your Podfile.\n\nFor Objective-C project:\n\n```\npod 'ZIKRouter', '\u003e= 1.1.1'\n\n# or only use ServiceRouter\npod 'ZIKRouter/ServiceRouter' , '\u003e=1.1.1'\n```\nFor Swift project:\n\n```\npod 'ZRouter', '\u003e= 1.1.1'\n\n# or only use ServiceRouter\npod 'ZRouter/ServiceRouter' , '\u003e=1.1.1'\n```\n\n### Carthage\n\nAdd this to your Cartfile:\n\n```\ngithub \"Zuikyo/ZIKRouter\" \u003e= 1.1.1\n```\n\nBuild frameworks:\n\n```\ncarthage update\n```\n\nBuild DEBUG version to enable route checking:\n\n```\ncarthage update --configuration Debug\n```\nRemember to use release version in production environment.\n\nFor Objective-C project, use `ZIKRouter.framework`. For Swift project, use `ZRouter.framework`.\n\n## Getting Started\n\nThis is the demo view controller and protocol：\n\n```swift\n///Editor view's interface\nprotocol EditorViewInput: class {\n    weak var delegate: EditorDelegate? { get set }\n    func constructForCreatingNewNote()\n}\n\n///Editor view controller\nclass NoteEditorViewController: UIViewController, EditorViewInput {\n    ...\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n///editor view's interface\n@protocol EditorViewInput \u003cZIKViewRoutable\u003e\n@property (nonatomic, weak) id\u003cEditorDelegate\u003e delegate;\n- (void)constructForCreatingNewNote;\n@end\n```\n\n```objectivec\n///Editor view controller\n@interface NoteEditorViewController: UIViewController \u003cEditorViewInput\u003e\n@end\n@implementation NoteEditorViewController\n@end\n```\n\n\u003c/details\u003e\n\n\nThere're 2 steps to create route for your module.\n\n### 1. Create Router\n\nTo make your class become modular, you need to create router for your module. You don't need to modify the module's code. That will reduce the cost for refactoring existing modules.\n\n#### 1.1 Router Subclass\n\nCreate router subclass for your module:\n\n```swift\nimport ZIKRouter.Internal\nimport ZRouter\n\nclass NoteEditorViewRouter: ZIKViewRouter\u003cNoteEditorViewController, ViewRouteConfig\u003e {\n    override class func registerRoutableDestination() {\n        // Register class with this router. A router can register multi views, and a view can be registered with multi routers\n        registerView(NoteEditorViewController.self)\n        // Register protocol. Then we can fetch this router with the protocol\n        register(RoutableView\u003cEditorViewInput\u003e())\n    }\n    \n    // Return the destination module\n    override func destination(with configuration: ViewRouteConfig) -\u003e NoteEditorViewController? {\n        // In configuration, you can get parameters from the caller for creating the instance\n        let destination: NoteEditorViewController? = ... /// instantiate your view controller\n        return destination\n    }\n    \n    override func prepareDestination(_ destination: NoteEditorViewController, configuration: ViewRouteConfig) {\n        // Inject dependencies to destination\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n//NoteEditorViewRouter.h\n@import ZIKRouter;\n\n@interface NoteEditorViewRouter : ZIKViewRouter\n@end\n\n//NoteEditorViewRouter.m\n@import ZIKRouter.Internal;\n\n@implementation NoteEditorViewRouter\n\n+ (void)registerRoutableDestination {\n    // Register class with this router. A router can register multi views, and a view can be registered with multi routers\n    [self registerView:[NoteEditorViewController class]];\n    // Register protocol. Then we can fetch this router with the protocol\n    [self registerViewProtocol:ZIKRoutable(EditorViewInput)];\n}\n\n// Return the destination module\n- (NoteEditorViewController *)destinationWithConfiguration:(ZIKViewRouteConfiguration *)configuration {\n    // In configuration, you can get parameters from the caller for creating the instance \n    NoteEditorViewController *destination = ... // instantiate your view controller\n    return destination;\n}\n\n- (void)prepareDestination:(NoteEditorViewController *)destination configuration:(ZIKViewRouteConfiguration *)configuration {\n    // Inject dependencies to destination\n}\n\n@end\n```\n\n\u003c/details\u003e\n\nEach router can control their own routing, such as using different custom transition. And the router can be very easy to add additional features.\n\nRead the documentation for more details and more methods to override.\n\n#### 1.2 Simple Router\n\nIf your module is very simple and don't need a router subclass, you can just register the class in a simpler way:\n\n```swift\nZIKAnyViewRouter.register(RoutableView\u003cEditorViewInput\u003e(), forMakingView: NoteEditorViewController.self)\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n[ZIKViewRouter registerViewProtocol:ZIKRoutable(EditorViewInput) forMakingView:[NoteEditorViewController class]];\n```\n\n\u003c/details\u003e\n\nor with custom creating block:\n\n```swift\nZIKAnyViewRouter.register(RoutableView\u003cEditorViewInput\u003e(), \n                 forMakingView: NoteEditorViewController.self) { (config, router) -\u003e EditorViewInput? in\n                     let destination: NoteEditorViewController? = ... // instantiate your view controller\n                     return destination;\n        }\n\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n[ZIKViewRouter\n    registerViewProtocol:ZIKRoutable(EditorViewInput)\n    forMakingView:[NoteEditorViewController class]\n    making:^id _Nullable(ZIKViewRouteConfiguration *config, ZIKViewRouter *router) {\n        NoteEditorViewController *destination = ... // instantiate your view controller\n        return destination;\n }];\n```\n\n\u003c/details\u003e\n\nor with custom factory function:\n\n```swift\nfunction makeEditorViewController(config: ViewRouteConfig) -\u003e EditorViewInput? {\n    let destination: NoteEditorViewController? = ... // instantiate your view controller\n    return destination;\n}\n\nZIKAnyViewRouter.register(RoutableView\u003cEditorViewInput\u003e(), \n                 forMakingView: NoteEditorViewController.self, making: makeEditorViewController)\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\nid\u003cEditorViewInput\u003e makeEditorViewController(ZIKViewRouteConfiguration *config) {\n    NoteEditorViewController *destination = ... // instantiate your view controller\n    return destination;\n}\n\n[ZIKViewRouter\n    registerViewProtocol:ZIKRoutable(EditorViewInput)\n    forMakingView:[NoteEditorViewController class]\n    factory:makeEditorViewController];\n```\n\n\u003c/details\u003e\n\n### 2. Declare Routable Type\n\nThe declaration is for checking routes at compile time, and supporting storyboard.\n\n```swift\n// Declare NoteEditorViewController is routable\n// This means there is a router for NoteEditorViewController\nextension NoteEditorViewController: ZIKRoutableView {\n}\n\n// Declare EditorViewInput is routable\n// This means you can use EditorViewInput to fetch router\nextension RoutableView where Protocol == EditorViewInput {\n    init() { self.init(declaredProtocol: Protocol.self) }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n// Declare NoteEditorViewController is routable\n// This means there is a router for NoteEditorViewController\nDeclareRoutableView(NoteEditorViewController, NoteEditorViewRouter)\n\n// If the protocol inherits from ZIKViewRoutable, it's routable\n// This means you can use EditorViewInput to fetch router\n@protocol EditorViewInput \u003cZIKViewRoutable\u003e\n@property (nonatomic, weak) id\u003cEditorDelegate\u003e delegate;\n- (void)constructForCreatingNewNote;\n@end\n```\n\n\u003c/details\u003e\n\n**If you use an undeclared protocol for routing, there will be compile time error. So it's much safer and easier to manage protocols and to know which protocols are routable.**\n\nUnroutable error in Swift:\n\n![Unroutable-error-Swift](Documentation/Resources/Unroutable-error-Swift.png)\n\nUnroutable error in Objective-C:\n\n![Unroutable-error-OC](Documentation/Resources/Unroutable-error-OC.png)\n\nNow you can get and show `NoteEditorViewController` with router.\n\n### View Router\n\n#### Transition directly\n\nTransition to editor view directly:\n\n```swift\nclass TestViewController: UIViewController {\n\n    // Transition to editor view directly\n    func showEditorDirectly() {\n        Router.perform(to: RoutableView\u003cEditorViewInput\u003e(), path: .push(from: self))\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@implementation TestViewController\n\n- (void)showEditorDirectly {\n    // Transition to editor view directly\n    [ZIKRouterToView(EditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];\n}\n\n@end\n```\n\n\u003c/details\u003e\n\nYou can change transition type with `ViewRoutePath`:\n\n```swift\nenum ViewRoutePath {\n    case push(from: UIViewController)\n    case presentModally(from: UIViewController)\n    case presentAsPopover(from: UIViewController, configure: ZIKViewRoutePopoverConfigure)\n    case performSegue(from: UIViewController, identifier: String, sender: Any?)\n    case show(from: UIViewController)\n    case showDetail(from: UIViewController)\n    case addAsChildViewController(from: UIViewController, addingChildViewHandler: (UIViewController, @escaping () -\u003e Void) -\u003e Void)\n    case addAsSubview(from: UIView)\n    case custom(from: ZIKViewRouteSource?)\n    case makeDestination\n    case extensible(path: ZIKViewRoutePath)\n}\n```\n\nEncapsulating view transition can hide the UIKit detail, then you can perform route outside the view layer (presenter, view model, interactor, service) and be cross-platform.\n\n#### Prepare before Transition\n\nPrepare it before transition to editor view:\n\n```swift\nclass TestViewController: UIViewController {\n\n    // Transition to editor view, and prepare the destination with EditorViewInput\n    func showEditor() {\n        Router.perform(\n            to: RoutableView\u003cEditorViewInput\u003e(),\n            path: .push(from: self),\n            configuring: { (config, _) in\n                // Route config\n                // Prepare the destination before transition\n                config.prepareDestination = { [weak self] destination in\n                    //destination is inferred as EditorViewInput\n                    destination.delegate = self\n                    destination.constructForCreatingNewNote()\n                }\n                config.successHandler = { destination in\n                    // Transition succeed\n                }\n                config.errorHandler = { (action, error) in\n                    // Transition failed\n                }                \n        })\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@implementation TestViewController\n\n- (void)showEditor {\n    // Transition to editor view, and prepare the destination with EditorViewInput\n    [ZIKRouterToView(EditorViewInput)\n\t     performPath:ZIKViewRoutePath.pushFrom(self)\n\t     configuring:^(ZIKViewRouteConfig *config) {\n\t         // Route config\n\t         // Prepare the destination before transition\n\t         config.prepareDestination = ^(id\u003cEditorViewInput\u003e destination) {\n\t             destination.delegate = self;\n\t             [destination constructForCreatingNewNote];\n\t         };\n\t         config.successHandler = ^(id\u003cEditorViewInput\u003e destination) {\n\t             // Transition is completed\n\t         };\n\t         config.errorHandler = ^(ZIKRouteAction routeAction, NSError * error) {\n\t             // Transition failed\n\t         };\n\t     }];\n}\n\n@end\n```\n\n\u003c/details\u003e\n\nFor more detail, read [Perform Route](Documentation/English/PerformRoute.md).\n\n#### Make Destination\n\nIf you don't want to show a view, but only need to get instance of the module, you can use `makeDestination`:\n\n```swift\n// destination is inferred as EditorViewInput\nlet destination = Router.makeDestination(to: RoutableView\u003cEditorViewInput\u003e())\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\nid\u003cEditorViewInput\u003e destination = [ZIKRouterToView(EditorViewInput) makeDestination];\n```\n\u003c/details\u003e\n\n#### Required Parameter and Special Parameter\n\nSome parameters can't be delivered though destination's protocol:\n\n* the destination class uses custom initializers to create instance, router needs to get required parameter from the caller\n\n* the module contains multi components, and you need to pass parameters to those components. Those parameters do not belong to the destination, so they should not exist in destination's protocol\n\nYou can use module config protocol and a custom configuration to transfer parameters.\n\nInstead of  `EditorViewInput`, we use another routable protocol `EditorViewModuleInput`  as config protocol for routing:\n\n```swift\n// In general, a module config protocol only contains `makeDestinationWith`, for declaring parameters and destination type. You can also add other properties or methods\nprotocol EditorViewModuleInput: class {\n    // Factory method for transferring parameters and making destination\n    var makeDestinationWith: (_ note: Note) -\u003e EditorViewInput? { get }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n// In general, a module config protocol only contains `makeDestinationWith`, for declaring parameters and destination type. You can also add other properties or methods\n@protocol EditorViewModuleInput \u003cZIKViewModuleRoutable\u003e\n // Factory method for transferring parameters and making destination\n@property (nonatomic, copy, readonly) id\u003cEditorViewInput\u003e _Nullable(^makeDestinationWith)(Note *note);\n@end\n```\n\n\u003c/details\u003e\n\nThis configuration works like a factory for the destination with `EditorViewModuleInput` protocol. It declares parameters for creating the destination.\n\nNow the user can use the module with its module config protocol and transfer parameters:\n\n```swift\nvar note = ...\nRouter.makeDestination(to: RoutableViewModule\u003cEditorViewModuleInput\u003e()) { (config) in\n     // Transfer parameters and get EditorViewInput\n     let destination = config.makeDestinationWith(note)\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\nNote *note = ...\n[ZIKRouterToViewModule(EditorViewModuleInput)\n    performPath:ZIKViewRoutePath.showFrom(self)\n    configuring:^(ZIKViewRouteConfiguration\u003cEditorViewModuleInput\u003e *config) {\n        // Transfer parameters and get EditorViewInput\n        id\u003cEditorViewInput\u003e destination = config.makeDestinationWith(note);\n }];\n```\n\n\u003c/details\u003e\n\nFor more detail, read [Transfer Parameters with Custom Configuration](Documentation/English/CustomConfiguration.md).\n\n#### Perform on Destination\n\nIf you get a destination from other place, you can perform on the destination with its router.\n\nFor example, an UIViewController supports 3D touch, and implments `UIViewControllerPreviewingDelegate`:\n\n```swift\nclass SourceViewController: UIViewController, UIViewControllerPreviewingDelegate {\n    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -\u003e UIViewController? {\n        // Return the destination UIViewController to let system preview it\n        let destination = Router.makeDestination(to: RoutableView\u003cEditorViewInput\u003e())\n        return destination\n    }\n    \n    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {\n        guard let destination = viewControllerToCommit as? EditorViewInput else {\n            return\n        }\n        // Show the destination\n        Router.to(RoutableView\u003cEditorViewInput\u003e())?.perform(onDestination: destination, path: .presentModally(from: self))\n}\n\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@implementation SourceViewController\n\n- (nullable UIViewController *)previewingContext:(id \u003cUIViewControllerPreviewing\u003e)previewingContext viewControllerForLocation:(CGPoint)location {\n    //Return the destination UIViewController to let system preview it\n    UIViewController\u003cEditorViewInput\u003e *destination = [ZIKRouterToView(EditorViewInput) makeDestination];\n    return destination;\n}\n\n- (void)previewingContext:(id \u003cUIViewControllerPreviewing\u003e)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {\n    // Show the destination\n    UIViewController\u003cEditorViewInput\u003e *destination;\n    if ([viewControllerToCommit conformsToProtocol:@protocol(EditorViewInput)]) {\n        destination = viewControllerToCommit;\n    } else {\n        return;\n    }\n    [ZIKRouterToView(EditorViewInput) performOnDestination:destination path:ZIKViewRoutePath.presentModallyFrom(self)];\n}\n\n@end\n```\n\n\u003c/details\u003e\n\n#### Prepare on Destination\n\nIf you don't want to show the destination, but just want to prepare an existing destination, you can prepare the destination with its router.\n\nIf the router injects dependencies inside it, this can properly setting the destination instance.\n\n```swift\nvar destination: DestinationViewInput = ...\nRouter.to(RoutableView\u003cEditorViewInput\u003e())?.prepare(destination: destination, configuring: { (config, _) in\n            config.prepareDestination = { destination in\n                // Prepare\n            }\n        })\n\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\nUIViewController\u003cEditorViewInput\u003e *destination = ...\n[ZIKRouterToView(EditorViewInput) prepareDestination:destination configuring:^(ZIKViewRouteConfiguration *config) {\n            config.prepareDestination = ^(id\u003cEditorViewInput\u003e destination) {\n                // Prepare\n            };\n        }];\n```\n\n\u003c/details\u003e\n\n#### Remove\n\nYou can remove the view by `removeRoute`, without using pop / dismiss / removeFromParentViewController / removeFromSuperview:\n\n```swift\nclass TestViewController: UIViewController {\n    var router: DestinationViewRouter\u003cEditorViewInput\u003e?\n    \n    func showEditor() {\n        // Hold the router\n        router = Router.perform(to: RoutableView\u003cEditorViewInput\u003e(), path: .push(from: self))\n    }\n    \n    // Router will pop the editor view controller\n    func removeEditorDirectly() {\n        guard let router = router, router.canRemove else {\n            return\n        }\n        router.removeRoute()\n        router = nil\n    }\n    \n    func removeEditorWithResult() {\n        guard let router = router, router.canRemove else {\n            return\n        }\n        router.removeRoute(successHandler: {\n            print(\"remove success\")\n        }, errorHandler: { (action, error) in\n            print(\"remove failed, error: \\(error)\")\n        })\n        router = nil\n    }\n    \n    func removeEditorAndPrepare() {\n        guard let router = router, router.canRemove else {\n            return\n        }\n        router.removeRoute(configuring: { (config) in\n            config.animated = true\n            config.prepareDestination = { destination in\n                // Use destination before remove it\n            }\n        })\n        router = nil\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@interface TestViewController()\n@property (nonatomic, strong) ZIKDestinationViewRouter(id\u003cEditorViewInput\u003e) *router;\n@end\n@implementation TestViewController\n\n- (void)showEditorDirectly {\n    // Hold the router\n    self.router = [ZIKRouterToView(EditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];\n}\n\n// Router will pop the editor view controller\n- (void)removeEditorDirectly {\n    if (![self.router canRemove]) {\n        return;\n    }\n    [self.router removeRoute];\n    self.router = nil;\n}\n\n- (void)removeEditorWithResult {\n    if (![self.router canRemove]) {\n        return;\n    }\n    [self.router removeRouteWithSuccessHandler:^{\n        NSLog(@\"pop success\");\n    } errorHandler:^(ZIKRouteAction routeAction, NSError *error) {\n        NSLog(@\"pop failed,error: %@\",error);\n    }];\n    self.router = nil;\n}\n\n- (void)removeEditorAndPrepare {\n    if (![self.router canRemove]) {\n        return;\n    }\n    [self.router removeRouteWithConfiguring:^(ZIKViewRemoveConfiguration *config) {\n        config.animated = YES;\n        config.prepareDestination = ^(UIViewController\u003cEditorViewInput\u003e *destination) {\n            // Use destination before remove it\n        };\n    }];\n    self.router = nil;\n}\n\n@end\n```\n\n\u003c/details\u003e\n\nFor more detail, read [Remove Route](Documentation/English/RemoveRoute.md).\n\n### Adapter\n\nYou can use another protocol to get router, as long as the protocol provides the same interface of the real protocol. Even the protocol is little different from the real protocol, you can  adapt two protocols with category, extension and proxy.\n\nRequired protocol used by the user:\n\n```swift\n/// Required protocol to use editor module\nprotocol RequiredEditorViewInput: class {\n    weak var delegate: EditorDelegate? { get set }\n    func constructForCreatingNewNote()\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n/// Required protocol to use editor module\n@protocol RequiredEditorViewInput \u003cZIKViewRoutable\u003e\n@property (nonatomic, weak) id\u003cEditorDelegate\u003e delegate;\n- (void)constructForCreatingNewNote;\n@end\n```\n\n\u003c/details\u003e\n\nIn the host app context, connect required protocol and provided protocol:\n```swift\n/// In the host app, add required protocol to editor router\nclass EditorViewAdapter: ZIKViewRouteAdapter {\n    override class func registerRoutableDestination() {\n        // If you can get the router, you can just register RequiredEditorViewInput to it\n        NoteEditorViewRouter.register(RoutableView\u003cRequiredEditorViewInput\u003e())\n        \n        // If you don't know the router, you can use adapter\n        register(adapter: RoutableView\u003cRequiredEditorViewInput\u003e(), forAdaptee: RoutableView\u003cEditorViewInput\u003e())\n    }\n}\n\n/// Make NoteEditorViewController conform to RequiredEditorViewInput\nextension NoteEditorViewController: RequiredEditorViewInput {\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n/// In the host app, add required protocol to editor router\n\n//EditorViewAdapter.h\n@interface EditorViewAdapter : ZIKViewRouteAdapter\n@end\n\n//EditorViewAdapter.m\n@implementation EditorViewAdapter\n\n+ (void)registerRoutableDestination {\n\t// If you can get the router, you can just register RequiredEditorViewInput to it\n\t[NoteEditorViewRouter registerViewProtocol:ZIKRoutable(RequiredEditorViewInput)];\n\t// If you don't know the router, you can use adapter\n\t[self registerDestinationAdapter:ZIKRoutable(RequiredEditorViewInput) forAdaptee:ZIKRoutable(EditorViewInput)];\n}\n\n@end\n\n/// Make NoteEditorViewController conform to RequiredEditorViewInput\n@interface NoteEditorViewController (Adapter) \u003cRequiredEditorViewInput\u003e\n@end\n@implementation NoteEditorViewController (Adapter)\n@end\n```\n\n\u003c/details\u003e\n\nAfter adapting, `RequiredEditorViewInput` and `EditorViewInput` can get the same router.\n\nUse`RequiredEditorViewInput`to get module:\n\n```swift\nclass TestViewController: UIViewController {\n\n    func showEditorDirectly() {\n        Router.perform(to: RoutableView\u003cRequiredEditorViewInput\u003e(), path: .push(from: self))\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@implementation TestViewController\n\n- (void)showEditorDirectly {\n    [ZIKRouterToView(RequiredEditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];\n}\n\n@end\n```\n\u003c/details\u003e\n\nUse `required protocol` and `provided protocol` to perfectly decouple modules, adapt interface and declare dependencies of the module. And you don't have to use a public header to manage those protocols.\n\n### Modularization\n\nSeparating `required protocol` and `provided protocol` makes your code truly modular. The caller declares its `required protocol`, and the provided module can easily be replaced by another module with the same `required protocol`.\n\nRead the `ZIKLoginModule` module in demo. The login module depends on an alert module, and the alert module is different in `ZIKRouterDemo ` and `ZIKRouterDemo-macOS`. You can change the provided module without changing anything in the login module.\n\nFor more detail, read [Module Adapter](Documentation/English/ModuleAdapter.md).\n\n### URL Router\n\nZIKRouter also provides a default URLRouter. It's easy to communicate with modules via url.\n\nURLRouter is not contained by default. If you want to use it, add submodule `pod 'ZIKRouter/URLRouter'` to your  `Podfile` , and call `[ZIKRouter enableDefaultURLRouteRule]` to enable URLRouter.\n\nYou can register router with a url:\n\n```swift\nclass NoteEditorViewRouter: ZIKViewRouter\u003cNoteEditorViewController, ViewRouteConfig\u003e {\n    override class func registerRoutableDestination() {\n        registerView(NoteEditorViewController.self)\n        register(RoutableView\u003cEditorViewInput\u003e())\n        // Register url\n        registerURLPattern(\"app://editor/:title\")\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n@implementation NoteEditorViewRouter\n\n+ (void)registerRoutableDestination {\n    [self registerView:[NoteEditorViewController class]];\n    [self registerViewProtocol:ZIKRoutable(EditorViewInput)];\n    // Register url\n    [self registerURLPattern:@\"app://editor/:title\"];\n}\n\n@end\n```\n\n\u003c/details\u003e\n\nThen you can get the router with it's url:\n\n```swift\nZIKAnyViewRouter.performURL(\"app://editor/test_note\", path: .push(from: self))\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n[ZIKAnyViewRouter performURL:@\"app://editor/test_note\" path:ZIKViewRoutePath.pushFrom(self)];\n```\n\n\u003c/details\u003e\n\nAnd handle URL Scheme:\n\n```swift\npublic func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -\u003e Bool {\n    let urlString = url.absoluteString\n    if let _ = ZIKAnyViewRouter.performURL(urlString, fromSource: self.rootViewController) {\n        return true\n    } else if let _ = ZIKAnyServiceRouter.performURL(urlString) {\n        return true\n    } else {\n        return false\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary\u003cUIApplicationOpenURLOptionsKey,id\u003e *)options {\n    if ([ZIKAnyViewRouter performURL:urlString fromSource:self.rootViewController]) {\n        return YES;\n    } else if ([ZIKAnyServiceRouter performURL:urlString]) {\n        return YES;\n    } else {\n        return NO;\n    }\n}\n```\n\n\u003c/details\u003e\n\nIf your project has different requirements for URL router, you can write your URL router by yourself. You can create custom ZIKRouter as parent class, add more powerful features in it. See `ZIKRouter+URLRouter.h`.\n\n### Other Features\n\nThere're other features, you can get details in the documentation:\n\n- [Custom Transition](Documentation/English/PerformRoute.md#Custom-Transition) in each router, such as switching view controller in tab bar\n- [Storyboard](Documentation/English/Storyboard.md)\n-  [AOP](Documentation/English/AOP.md) callback in view transition\n- [Handle Custom Event](Documentation/English/PerformRoute.md#Custom-Event)\n\n### Service Router\n\nInstead of view, you can also get any service modules:\n\n```swift\n/// time service's interface\nprotocol TimeServiceInput {\n    func currentTimeString() -\u003e String\n}\n```\n```swift\nclass TestViewController: UIViewController {\n    @IBOutlet weak var timeLabel: UILabel!\n    \n    func callTimeService() {\n        // Get the service for TimeServiceInput\n        let timeService = Router.makeDestination(\n            to: RoutableService\u003cTimeServiceInput\u003e(),\n            preparation: { destination in\n            // prepare the service if needed\n        })\n        //Use the service\n        timeLabel.text = timeService.currentTimeString()\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003eObjective-C Sample\u003c/summary\u003e\n\n```objectivec\n/// time service's interface\n@protocol TimeServiceInput \u003cZIKServiceRoutable\u003e\n- (NSString *)currentTimeString;\n@end\n```\n\n```objectivec\n@interface TestViewController ()\n@property (weak, nonatomic) IBOutlet UILabel *timeLabel;\n@end\n\n@implementation TestViewController\n\n- (void)callTimeService {\n   // Get the service for TimeServiceInput\n   id\u003cTimeServiceInput\u003e timeService = [ZIKRouterToService(TimeServiceInput) makeDestination];\n   self.timeLabel.text = [timeService currentTimeString];    \n}\n\n```\n\n\u003c/details\u003e\n\n## Demo and Practice\n\nZIKRouter is designed for VIPER architecture at first. But you can also use it in MVC or anywhere.\n\nThe demo (ZIKRouterDemo) in this repository shows how to use ZIKRouter to perform each route type. Open `Router.xcworkspace` to run it.\n\nIf you want to see how it works in a VIPER architecture app, go to [ZIKViper](https://github.com/Zuikyo/ZIKViper).\n\n## File Template\n\nYou can use Xcode file template to create router and protocol code quickly:\n\n![File Template](Documentation/Resources/filetemplate.png)\n\nThe template `ZIKRouter.xctemplate` is in [Templates](Templates/).\n\nCopy `ZIKRouter.xctemplate` to `~/Library/Developer/Xcode/Templates/ZIKRouter.xctemplate`, then you can use it in `Xcode -\u003e File -\u003e New -\u003e File -\u003e Templates`.\n\n## License\n\nZIKRouter is available under the MIT license. See the LICENSE file for more info.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZuikyo%2FZIKRouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FZuikyo%2FZIKRouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FZuikyo%2FZIKRouter/lists"}