ZIKRouter 1.1.1

ZIKRouter 1.1.1

测试已测试
语言语言 Obj-CObjective C
许可协议 MIT
发布最后发布2019 年 8 月

zuik 维护。



ZIKRouter 1.1.1

  • Zuikyo

ZIKRouter

ZIKRouter Carthage compatible license

一个针对模块管理和通过协议进行依赖注入的接口导向路由器。

视图路由器可以通過一个方法执行 UIKit / AppKit 中所有导航类型。

服务路由器可以寻找到对应的模块并准备它,通过其协议。


这是一个用于模块解耦和通信的组件化路由工具,基于接口进行模块管理和依赖注入。通过多种方式最大限度地发挥编译检查的功能。

通过 protocol 寻找对应的模块,通过 protocol 进行依赖注入和模块通信。

服务路由器可以管理任意自定义模块。视图路由器封装了界面跳转。

中文文档


特性

  • 支持 Swift 和 Objective-C
  • 支持 iOS, macOS 和 tvOS
  • 创建路由器的文件模板
  • 支持 UIViewController / NSViewController, UIView / NSView 以及任何类的路由
  • 支持依赖注入,包括动态注入和静态注入
  • 通过声明可路由协议进行编译时检查。使用未声明的协议将会带来编译错误。这是最强大的特性之一
  • 通过其协议与模块进行匹配
  • 支持 URL 路由
  • 通过其协议配置模块,而不是通过参数字典
  • 需要和提供的协议用于彻底解耦
  • 适配器用于解耦模块并添加兼容接口
  • 支持 Storyboard。可以通过 segue 自动准备视图
  • 封装 UIKit / AppKit 中所有的过渡方法和 unwind 方法,以及自定义过渡
  • 对视图过渡进行检查
  • 对视图过渡支持 AOP
  • 内存泄漏检测
  • 自定义事件处理
  • 自动注册
  • 高度可扩展

快速入门指南

  1. 创建路由器
    1. 路由器子类
    2. 简单路由器
  2. 声明可路由类型
  3. 视图路由器
    1. 直接过渡
    2. 过渡前准备
    3. 创建目标
    4. 必选参数和特殊参数
    5. 在目标处执行
    6. 在目标处准备
    7. 移除
    8. 适配器
    9. 模块化
    10. URL 路由器
    11. 其他特性
  4. 服务路由器
  5. 演示和实践
  6. 文件模板

文档

设计理念

设计理念

基础

  1. 路由器实现
  2. 模块注册
  3. 可路由声明
  4. 类型检查
  5. 执行路由
  6. 删除路由
  7. 使用自定义配置传递参数

高级特性

  1. 错误处理
  2. 故事板和自动创建
  3. AOP
  4. 依赖注入
  5. 循环依赖
  6. 模块适配器
  7. 单元测试

FAQ

要求

  • iOS 7.0+
  • Swift 3.2+
  • Xcode 9.0+

安装

Cocoapods

将此内容添加到您的Podfile中。

针对Objective-C项目

pod 'ZIKRouter', '>= 1.1.1'

# or only use ServiceRouter
pod 'ZIKRouter/ServiceRouter' , '>=1.1.1'

针对Swift项目

pod 'ZRouter', '>= 1.1.1'

# or only use ServiceRouter
pod 'ZRouter/ServiceRouter' , '>=1.1.1'

Carthage

将此内容添加到您的Cartfile。

github "Zuikyo/ZIKRouter" >= 1.1.1

构建框架

carthage update

构建DEBUG版本以启用路由检查

carthage update --configuration Debug

请记住在生产环境中使用发布版本。

对于Objective-C项目,使用 ZIKRouter.framework。对于Swift项目,使用 ZRouter.framework

入门

这是一个示例视图控制器和协议:

///Editor view's interface
protocol EditorViewInput: class {
    weak var delegate: EditorDelegate? { get set }
    func constructForCreatingNewNote()
}

///Editor view controller
class NoteEditorViewController: UIViewController, EditorViewInput {
    ...
}
Objective-C 示例
///editor view's interface
@protocol EditorViewInput <ZIKViewRoutable>
@property (nonatomic, weak) id<EditorDelegate> delegate;
- (void)constructForCreatingNewNote;
@end
///Editor view controller
@interface NoteEditorViewController: UIViewController <EditorViewInput>
@end
@implementation NoteEditorViewController
@end

创建您的模块路由有两个步骤。

1. 创建路由

为了使您的类成为模块化,您需要为您的模块创建路由。您不需要修改模块的代码。这将会减少现有模块重构的成本。

1.1 路由子类

为您的模块创建路由子类

import ZIKRouter.Internal
import ZRouter

class NoteEditorViewRouter: ZIKViewRouter<NoteEditorViewController, ViewRouteConfig> {
    override class func registerRoutableDestination() {
        // Register class with this router. A router can register multi views, and a view can be registered with multi routers
        registerView(NoteEditorViewController.self)
        // Register protocol. Then we can fetch this router with the protocol
        register(RoutableView<EditorViewInput>())
    }
    
    // Return the destination module
    override func destination(with configuration: ViewRouteConfig) -> NoteEditorViewController? {
        // In configuration, you can get parameters from the caller for creating the instance
        let destination: NoteEditorViewController? = ... /// instantiate your view controller
        return destination
    }
    
    override func prepareDestination(_ destination: NoteEditorViewController, configuration: ViewRouteConfig) {
        // Inject dependencies to destination
    }
}
Objective-C 示例
//NoteEditorViewRouter.h
@import ZIKRouter;

@interface NoteEditorViewRouter : ZIKViewRouter
@end

//NoteEditorViewRouter.m
@import ZIKRouter.Internal;

@implementation NoteEditorViewRouter

+ (void)registerRoutableDestination {
    // Register class with this router. A router can register multi views, and a view can be registered with multi routers
    [self registerView:[NoteEditorViewController class]];
    // Register protocol. Then we can fetch this router with the protocol
    [self registerViewProtocol:ZIKRoutable(EditorViewInput)];
}

// Return the destination module
- (NoteEditorViewController *)destinationWithConfiguration:(ZIKViewRouteConfiguration *)configuration {
    // In configuration, you can get parameters from the caller for creating the instance 
    NoteEditorViewController *destination = ... // instantiate your view controller
    return destination;
}

- (void)prepareDestination:(NoteEditorViewController *)destination configuration:(ZIKViewRouteConfiguration *)configuration {
    // Inject dependencies to destination
}

@end

每个路由可以控制自己的路由,例如使用不同的自定义过渡。并且路由可以非常容易地添加附加功能。

阅读更多文档以获取详细信息以及更多的覆盖方法。

1.2 简单路由

如果您的模块非常简单且不需要路由子类,您只需以更简单的方式注册该类

ZIKAnyViewRouter.register(RoutableView<EditorViewInput>(), forMakingView: NoteEditorViewController.self)
Objective-C 示例
[ZIKViewRouter registerViewProtocol:ZIKRoutable(EditorViewInput) forMakingView:[NoteEditorViewController class]];

或者通过自定义创建块

ZIKAnyViewRouter.register(RoutableView<EditorViewInput>(), 
                 forMakingView: NoteEditorViewController.self) { (config, router) -> EditorViewInput? in
                     let destination: NoteEditorViewController? = ... // instantiate your view controller
                     return destination;
        }
Objective-C 示例
[ZIKViewRouter
    registerViewProtocol:ZIKRoutable(EditorViewInput)
    forMakingView:[NoteEditorViewController class]
    making:^id _Nullable(ZIKViewRouteConfiguration *config, ZIKViewRouter *router) {
        NoteEditorViewController *destination = ... // instantiate your view controller
        return destination;
 }];

或者通过自定义工厂函数

function makeEditorViewController(config: ViewRouteConfig) -> EditorViewInput? {
    let destination: NoteEditorViewController? = ... // instantiate your view controller
    return destination;
}

ZIKAnyViewRouter.register(RoutableView<EditorViewInput>(), 
                 forMakingView: NoteEditorViewController.self, making: makeEditorViewController)
Objective-C 示例
id<EditorViewInput> makeEditorViewController(ZIKViewRouteConfiguration *config) {
    NoteEditorViewController *destination = ... // instantiate your view controller
    return destination;
}

[ZIKViewRouter
    registerViewProtocol:ZIKRoutable(EditorViewInput)
    forMakingView:[NoteEditorViewController class]
    factory:makeEditorViewController];

2. 声明可路由类型

声明用于在编译时检查路由,并支持故事板。

// Declare NoteEditorViewController is routable
// This means there is a router for NoteEditorViewController
extension NoteEditorViewController: ZIKRoutableView {
}

// Declare EditorViewInput is routable
// This means you can use EditorViewInput to fetch router
extension RoutableView where Protocol == EditorViewInput {
    init() { self.init(declaredProtocol: Protocol.self) }
}
Objective-C 示例
// Declare NoteEditorViewController is routable
// This means there is a router for NoteEditorViewController
DeclareRoutableView(NoteEditorViewController, NoteEditorViewRouter)

// If the protocol inherits from ZIKViewRoutable, it's routable
// This means you can use EditorViewInput to fetch router
@protocol EditorViewInput <ZIKViewRoutable>
@property (nonatomic, weak) id<EditorDelegate> delegate;
- (void)constructForCreatingNewNote;
@end

如果您使用未声明的协议进行路由,则会发生编译时错误。因此,管理协议和了解哪些协议是可路由的更加安全且易于管理。

Swift中的不可路由错误

Unroutable-error-Swift

Objective-C中的不可路由错误

Unroutable-error-OC

现在您可以使用路由获取并显示NoteEditorViewController

视图路由器

直接过渡

直接过渡到编辑视图

class TestViewController: UIViewController {

    // Transition to editor view directly
    func showEditorDirectly() {
        Router.perform(to: RoutableView<EditorViewInput>(), path: .push(from: self))
    }
}
Objective-C 示例
@implementation TestViewController

- (void)showEditorDirectly {
    // Transition to editor view directly
    [ZIKRouterToView(EditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];
}

@end

您可以通过ViewRoutePath更改过渡类型

enum ViewRoutePath {
    case push(from: UIViewController)
    case presentModally(from: UIViewController)
    case presentAsPopover(from: UIViewController, configure: ZIKViewRoutePopoverConfigure)
    case performSegue(from: UIViewController, identifier: String, sender: Any?)
    case show(from: UIViewController)
    case showDetail(from: UIViewController)
    case addAsChildViewController(from: UIViewController, addingChildViewHandler: (UIViewController, @escaping () -> Void) -> Void)
    case addAsSubview(from: UIView)
    case custom(from: ZIKViewRouteSource?)
    case makeDestination
    case extensible(path: ZIKViewRoutePath)
}

封装视图过渡可以隐藏UIKit的细节,然后您可以在视图层之外(演示者、视图模型、交互者、服务)执行路由,并且是跨平台的。

在转换之前准备

在转换到编辑视图之前准备它

class TestViewController: UIViewController {

    // Transition to editor view, and prepare the destination with EditorViewInput
    func showEditor() {
        Router.perform(
            to: RoutableView<EditorViewInput>(),
            path: .push(from: self),
            configuring: { (config, _) in
                // Route config
                // Prepare the destination before transition
                config.prepareDestination = { [weak self] destination in
                    //destination is inferred as EditorViewInput
                    destination.delegate = self
                    destination.constructForCreatingNewNote()
                }
                config.successHandler = { destination in
                    // Transition succeed
                }
                config.errorHandler = { (action, error) in
                    // Transition failed
                }                
        })
    }
}
Objective-C 示例
@implementation TestViewController

- (void)showEditor {
    // Transition to editor view, and prepare the destination with EditorViewInput
    [ZIKRouterToView(EditorViewInput)
	     performPath:ZIKViewRoutePath.pushFrom(self)
	     configuring:^(ZIKViewRouteConfig *config) {
	         // Route config
	         // Prepare the destination before transition
	         config.prepareDestination = ^(id<EditorViewInput> destination) {
	             destination.delegate = self;
	             [destination constructForCreatingNewNote];
	         };
	         config.successHandler = ^(id<EditorViewInput> destination) {
	             // Transition is completed
	         };
	         config.errorHandler = ^(ZIKRouteAction routeAction, NSError * error) {
	             // Transition failed
	         };
	     }];
}

@end

更多详细信息,请参阅执行路线

创建目标

如果你不想显示视图,只需要获取模块的实例,可以使用makeDestination

// destination is inferred as EditorViewInput
let destination = Router.makeDestination(to: RoutableView<EditorViewInput>())
Objective-C 示例
id<EditorViewInput> destination = [ZIKRouterToView(EditorViewInput) makeDestination];

必需参数和特殊参数

一些参数无法通过目标协议传递

  • 目标类使用自定义初始化器来创建实例,路由器需要从调用者获取必需参数

  • 模块包含多个组件,你需要将这些参数传递给这些组件。这些参数不属于目标,因此不应存在于目标的协议中

您可以使用模块配置协议和自定义配置来传递参数。

我们不使用EditorViewInput,而使用另一个可路由协议EditorViewModuleInput作为路由配置协议

// In general, a module config protocol only contains `makeDestinationWith`, for declaring parameters and destination type. You can also add other properties or methods
protocol EditorViewModuleInput: class {
    // Factory method for transferring parameters and making destination
    var makeDestinationWith: (_ note: Note) -> EditorViewInput? { get }
}
Objective-C 示例
// In general, a module config protocol only contains `makeDestinationWith`, for declaring parameters and destination type. You can also add other properties or methods
@protocol EditorViewModuleInput <ZIKViewModuleRoutable>
 // Factory method for transferring parameters and making destination
@property (nonatomic, copy, readonly) id<EditorViewInput> _Nullable(^makeDestinationWith)(Note *note);
@end

此配置类似于具有EditorViewModuleInput协议的目标的工厂。它声明了用于创建目标的参数。

现在用户可以使用模块及其模块配置协议来传输参数

var note = ...
Router.makeDestination(to: RoutableViewModule<EditorViewModuleInput>()) { (config) in
     // Transfer parameters and get EditorViewInput
     let destination = config.makeDestinationWith(note)
}
Objective-C 示例
Note *note = ...
[ZIKRouterToViewModule(EditorViewModuleInput)
    performPath:ZIKViewRoutePath.showFrom(self)
    configuring:^(ZIKViewRouteConfiguration<EditorViewModuleInput> *config) {
        // Transfer parameters and get EditorViewInput
        id<EditorViewInput> destination = config.makeDestinationWith(note);
 }];

更多详细信息,请参阅使用自定义配置传输参数

在目标上执行

如果你从其他地方获取了目标,你可以使用其路由器在目标上执行。

例如,UIViewController支持3D touch,并实现了UIViewControllerPreviewingDelegate

class SourceViewController: UIViewController, UIViewControllerPreviewingDelegate {
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        // Return the destination UIViewController to let system preview it
        let destination = Router.makeDestination(to: RoutableView<EditorViewInput>())
        return destination
    }
    
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
        guard let destination = viewControllerToCommit as? EditorViewInput else {
            return
        }
        // Show the destination
        Router.to(RoutableView<EditorViewInput>())?.perform(onDestination: destination, path: .presentModally(from: self))
}
Objective-C 示例
@implementation SourceViewController

- (nullable UIViewController *)previewingContext:(id <UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
    //Return the destination UIViewController to let system preview it
    UIViewController<EditorViewInput> *destination = [ZIKRouterToView(EditorViewInput) makeDestination];
    return destination;
}

- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UIViewController *)viewControllerToCommit {
    // Show the destination
    UIViewController<EditorViewInput> *destination;
    if ([viewControllerToCommit conformsToProtocol:@protocol(EditorViewInput)]) {
        destination = viewControllerToCommit;
    } else {
        return;
    }
    [ZIKRouterToView(EditorViewInput) performOnDestination:destination path:ZIKViewRoutePath.presentModallyFrom(self)];
}

@end

在目的地准备

如果您不想显示目的地,但只想准备一个现有的目的地,您可以使用它的路由器来准备该目的地。

如果路由器在其内部注入依赖项,则可以正确配置目的地实例。

var destination: DestinationViewInput = ...
Router.to(RoutableView<EditorViewInput>())?.prepare(destination: destination, configuring: { (config, _) in
            config.prepareDestination = { destination in
                // Prepare
            }
        })
Objective-C 示例
UIViewController<EditorViewInput> *destination = ...
[ZIKRouterToView(EditorViewInput) prepareDestination:destination configuring:^(ZIKViewRouteConfiguration *config) {
            config.prepareDestination = ^(id<EditorViewInput> destination) {
                // Prepare
            };
        }];

删除

您可以通过 removeRoute 删除视图,而无需使用 pop / dismiss / removeFromParentViewController / removeFromSuperview。

class TestViewController: UIViewController {
    var router: DestinationViewRouter<EditorViewInput>?
    
    func showEditor() {
        // Hold the router
        router = Router.perform(to: RoutableView<EditorViewInput>(), path: .push(from: self))
    }
    
    // Router will pop the editor view controller
    func removeEditorDirectly() {
        guard let router = router, router.canRemove else {
            return
        }
        router.removeRoute()
        router = nil
    }
    
    func removeEditorWithResult() {
        guard let router = router, router.canRemove else {
            return
        }
        router.removeRoute(successHandler: {
            print("remove success")
        }, errorHandler: { (action, error) in
            print("remove failed, error: \(error)")
        })
        router = nil
    }
    
    func removeEditorAndPrepare() {
        guard let router = router, router.canRemove else {
            return
        }
        router.removeRoute(configuring: { (config) in
            config.animated = true
            config.prepareDestination = { destination in
                // Use destination before remove it
            }
        })
        router = nil
    }
}
Objective-C 示例
@interface TestViewController()
@property (nonatomic, strong) ZIKDestinationViewRouter(id<EditorViewInput>) *router;
@end
@implementation TestViewController

- (void)showEditorDirectly {
    // Hold the router
    self.router = [ZIKRouterToView(EditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];
}

// Router will pop the editor view controller
- (void)removeEditorDirectly {
    if (![self.router canRemove]) {
        return;
    }
    [self.router removeRoute];
    self.router = nil;
}

- (void)removeEditorWithResult {
    if (![self.router canRemove]) {
        return;
    }
    [self.router removeRouteWithSuccessHandler:^{
        NSLog(@"pop success");
    } errorHandler:^(ZIKRouteAction routeAction, NSError *error) {
        NSLog(@"pop failed,error: %@",error);
    }];
    self.router = nil;
}

- (void)removeEditorAndPrepare {
    if (![self.router canRemove]) {
        return;
    }
    [self.router removeRouteWithConfiguring:^(ZIKViewRemoveConfiguration *config) {
        config.animated = YES;
        config.prepareDestination = ^(UIViewController<EditorViewInput> *destination) {
            // Use destination before remove it
        };
    }];
    self.router = nil;
}

@end

有关更多详细信息,请阅读 删除路由

适配器

只要协议提供了与实际协议相同的接口,您就可以使用另一个协议来获取路由器。即使协议与实际协议略有不同,您也可以通过分类、扩展和代理来适配两个协议。

用户使用的所需协议

/// Required protocol to use editor module
protocol RequiredEditorViewInput: class {
    weak var delegate: EditorDelegate? { get set }
    func constructForCreatingNewNote()
}
Objective-C 示例
/// Required protocol to use editor module
@protocol RequiredEditorViewInput <ZIKViewRoutable>
@property (nonatomic, weak) id<EditorDelegate> delegate;
- (void)constructForCreatingNewNote;
@end

在宿主应用程序上下文中,连接所需的协议和提供的协议

/// In the host app, add required protocol to editor router
class EditorViewAdapter: ZIKViewRouteAdapter {
    override class func registerRoutableDestination() {
        // If you can get the router, you can just register RequiredEditorViewInput to it
        NoteEditorViewRouter.register(RoutableView<RequiredEditorViewInput>())
        
        // If you don't know the router, you can use adapter
        register(adapter: RoutableView<RequiredEditorViewInput>(), forAdaptee: RoutableView<EditorViewInput>())
    }
}

/// Make NoteEditorViewController conform to RequiredEditorViewInput
extension NoteEditorViewController: RequiredEditorViewInput {
}
Objective-C 示例
/// In the host app, add required protocol to editor router

//EditorViewAdapter.h
@interface EditorViewAdapter : ZIKViewRouteAdapter
@end

//EditorViewAdapter.m
@implementation EditorViewAdapter

+ (void)registerRoutableDestination {
	// If you can get the router, you can just register RequiredEditorViewInput to it
	[NoteEditorViewRouter registerViewProtocol:ZIKRoutable(RequiredEditorViewInput)];
	// If you don't know the router, you can use adapter
	[self registerDestinationAdapter:ZIKRoutable(RequiredEditorViewInput) forAdaptee:ZIKRoutable(EditorViewInput)];
}

@end

/// Make NoteEditorViewController conform to RequiredEditorViewInput
@interface NoteEditorViewController (Adapter) <RequiredEditorViewInput>
@end
@implementation NoteEditorViewController (Adapter)
@end

适应后,RequiredEditorViewInputEditorViewInput 可以获取相同的路由器。

使用 RequiredEditorViewInput 获取模块

class TestViewController: UIViewController {

    func showEditorDirectly() {
        Router.perform(to: RoutableView<RequiredEditorViewInput>(), path: .push(from: self))
    }
}
Objective-C 示例
@implementation TestViewController

- (void)showEditorDirectly {
    [ZIKRouterToView(RequiredEditorViewInput) performPath:ZIKViewRoutePath.pushFrom(self)];
}

@end

使用 required 协议provided 协议 完美地解耦模块,适配接口并声明模块的依赖关系。而且您无需使用公共头文件来管理这些协议。

模块化

分隔 required 协议provided 协议 使您的代码更加模块化。调用者声明其 required 协议,而提供的模块可以很容易地被具有相同 required 协议 的另一个模块替换。

查看示例中的 ZIKLoginModule 模块。登录模块依赖于一个警报模块,而警报模块在 ZIKRouterDemo ZIKRouterDemo-macOS 中不同。您可以更改提供的模块,而不需要更改登录模块中的任何内容。

有关更多详细信息,请阅读 模块适配器

URL 路由器

ZIKRouter 还提供了一个默认的 URL 路由器。它通过 URL 与模块通信很容易。

URL 路由器默认不包含在其中。如果你想使用它,请在你的 Podfile 中添加子模块 pod 'ZIKRouter/URLRouter',并通过调用 [ZIKRouter enableDefaultURLRouteRule] 来启用 URL 路由器。

你可以使用 URL 来注册路由器

class NoteEditorViewRouter: ZIKViewRouter<NoteEditorViewController, ViewRouteConfig> {
    override class func registerRoutableDestination() {
        registerView(NoteEditorViewController.self)
        register(RoutableView<EditorViewInput>())
        // Register url
        registerURLPattern("app://editor/:title")
    }
}
Objective-C 示例
@implementation NoteEditorViewRouter

+ (void)registerRoutableDestination {
    [self registerView:[NoteEditorViewController class]];
    [self registerViewProtocol:ZIKRoutable(EditorViewInput)];
    // Register url
    [self registerURLPattern:@"app://editor/:title"];
}

@end

然后你可以通过它的 URL 来获取路由器

ZIKAnyViewRouter.performURL("app://editor/test_note", path: .push(from: self))
Objective-C 示例
[ZIKAnyViewRouter performURL:@"app://editor/test_note" path:ZIKViewRoutePath.pushFrom(self)];

并处理 URL 方案

public func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    let urlString = url.absoluteString
    if let _ = ZIKAnyViewRouter.performURL(urlString, fromSource: self.rootViewController) {
        return true
    } else if let _ = ZIKAnyServiceRouter.performURL(urlString) {
        return true
    } else {
        return false
    }
}
Objective-C 示例
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    if ([ZIKAnyViewRouter performURL:urlString fromSource:self.rootViewController]) {
        return YES;
    } else if ([ZIKAnyServiceRouter performURL:urlString]) {
        return YES;
    } else {
        return NO;
    }
}

如果你的项目对 URL 路由器有不同要求,你可以自己编写 URL 路由器。你可以创建自定义的 ZIKRouter 作为父类,并在其中添加更多强大的功能。请参阅 ZIKRouter+URLRouter.h

其他功能

还有其他功能,你可以在文档中找到详细信息。

服务路由器

除了视图外,你还可以获取任何服务模块

/// time service's interface
protocol TimeServiceInput {
    func currentTimeString() -> String
}
class TestViewController: UIViewController {
    @IBOutlet weak var timeLabel: UILabel!
    
    func callTimeService() {
        // Get the service for TimeServiceInput
        let timeService = Router.makeDestination(
            to: RoutableService<TimeServiceInput>(),
            preparation: { destination in
            // prepare the service if needed
        })
        //Use the service
        timeLabel.text = timeService.currentTimeString()
    }
}
Objective-C 示例
/// time service's interface
@protocol TimeServiceInput <ZIKServiceRoutable>
- (NSString *)currentTimeString;
@end
@interface TestViewController ()
@property (weak, nonatomic) IBOutlet UILabel *timeLabel;
@end

@implementation TestViewController

- (void)callTimeService {
   // Get the service for TimeServiceInput
   id<TimeServiceInput> timeService = [ZIKRouterToService(TimeServiceInput) makeDestination];
   self.timeLabel.text = [timeService currentTimeString];    
}

示例和练习

ZIKRouter 最初是为 VIPER 架构设计的。但你也也可以在 MVC 或其他任何地方使用它。

这个仓库中的示例(ZIKRouterDemo)展示了如何使用 ZIKRouter 执行每种路由类型。打开 Router.xcworkspace 来运行它。

如果你想看它在一个 VIPER 架构应用中的工作方式,请访问 ZIKViper

文件模板

你可以使用 Xcode 文件模板快速创建路由器和协议代码

File Template

模板 ZIKRouter.xctemplate模板 位置。

ZIKRouter.xctemplate 复制到 ~/Library/Developer/Xcode/Templates/ZIKRouter.xctemplate,然后在 Xcode -> 文件 -> 新建 -> 文件 -> 模板 中使用它。

许可

ZIKRouter 在 MIT 许可下可用。更多信息请查看 LICENSE 文件。