Skip to content

快速开始

Rakuyo edited this page Apr 26, 2020 · 12 revisions

下面的内容将告诉您如何使用该库,但基本不会涉及 "为什么这么做" 以及 "这个类型是什么意思"。

有关更多详细信息,请参见其他 wiki。有关完整的代码示例,请参阅随仓库配套的 demo(在 Examples 目录下)。

面向协议

RaRouter 基于面向协议原则进行开发。在正式开始之前,希望本图能让您对 RaRouter 各协议之间的组织关系有一个初步的了解:

执行路由

RaRouter 默认提供以下三种路由操作:

  • do: 执行某些操作:
let router = "rakuyo://moduleA/do/something"

_ = Router<Global>.do(router, param: ("参数1", 2))
  • get:执行某些操作,并获得其返回值:
let router = "rakuyo://moduleA/calculate/frame"

let result = Router<Global>.get(of: String.self, from: router, param: "参数")

switch result {
case .success(let string):
    print(string)
    
case .failure(let error):
    print(error)
}

// Or use default values
let string = Router<Global>.get(of: String.self, from: router, param: "参数").get(default: "defaultString")

// When an error occurs, "defaultString" will be printed
print(string)
  • viewController:执行某些操作,并获得其返回的 UIViewController 子类:
let router = "rakuyo://moduleA/create"

let result = Router<Global>.viewController(from: router)

switch result {
case .success(let controller):
    print(controller) // `UIViewController`
    
case .failure(let error):
    print(error)
}

一些解释:

  1. Router 类型是 RaRouter 提供的一个遵守 RaRouter 协议的默认类型,用以执行、注册路由。它的声明请参考:Router

  2. 关于 RaRouter 协议的相关内容,请参考 wiki 或随仓库配套的 demo。

  3. Router 类型需要一个泛型,该泛型代表着路由所属的模块。而 Global 泛型代表着 “全局模块”。相关的概念及内容将在 进阶教程 中详细介绍。

  4. 对于 param 参数,其为 Any? 类型,默认为 nil。这意味着您可以传递任何您想要的参数,并且不受任何的限制。

    例如在上面 do 的示例中,传入了一个 (String, Int) 类型的元组作为参数,而在 get 示例中只传入了一个 String。到了 viewController,因为从编码上来说 "rakuyo://moduleA/create" 不需要任何参数(假如编写了那些代码),所以可以直接省略 param 参数。

  5. viewController 操作没有像 get 操作一样,在方法中添加 type 参数用以转换类型。

    因为 Controller 的定义往往在核心组件中,而 RaRouter 要求路由组件独立且尽可能的不去依赖其他组件。所以在一般情况下,在调用 viewController 方法时,用户是无法获取 ViewController 的真实类型的。

    而执行 get 操作时,因为我们可以将模型类定义在路由组件,所以设计了 type 参数,并提供了转换失败时的相关错误

封装

我们还可以再优化一下上面的代码,例如封装一下 router 路由字符串:

RaRouter 提供了 ModuleRouterRouterTableProtocol 协议,用户可以借助这两个协议定义一个模块:

public enum ModuleA: ModuleRouter {
    
    public typealias Table = RouterTable
    
    public enum RouterTable: String, RouterTableProtocol {
        
        public var url: String { rawValue }
        
        case create         = "rakuyo://moduleA/create"
        case doSomething    = "rakuyo://moduleA/do/something"
        case calculateFrame = "rakuyo://moduleA/calculate/frame" 
    }
}

紧接着,还可以再借助 RaRouter 协议的泛型 Module,封装路由的执行过程:

其中,DoResultGetResult<T> 以及 ViewControllerResult 的详细说明请参考 wiki 或随仓库配套的 demo。

public extension Router where Module == ModuleA {
    
    static func doSomething(start: Date, end: Date) -> DoResult {
        return Router.do(.doSomething, param: (start, end))
    }

    static func calculateFrame(with screenWidth: CGFloat) -> GetResult<CGRect> {
        return Router.get(of: CGRect.self, from: .calculateFrame, param: screenWidth)
    }
    
    static func create() -> ViewControllerResult {
        return Router.viewController(from: .create)
    }
}

当我们再执行 执行路由 一节的示例代码时,就可以调用封装好的方法:

// for `do`
_ = Router<ModuleA>.doSomething(start: Date(), end: Date())

// for `get`
if case let .success(frame) = Router<ModuleA>.calculateFrame(with: 375) { }

// for `viewController`
if case let .success(controller) = Router<ModuleA>.create() { }

注册

最后,我们需要注册刚刚定义好的路由。

对于默认提供的三种操作,每种操作都有不同的注册方法。而它们的区别在于闭包的返回值不同:

  • do

其闭包的返回值规定为 DoResultResult<Void, RouterError>):

Router<Global>.register(for: "your router") { (url, value) -> DoResult in
    // do something
    return .success(())
}
  • get

其闭包的返回值规定为 GetResult<AnyResult>Result<Any?, RouterError>):

Router<Global>.register(for: "your router") { (url, value) -> GetResult<AnyResult> in
    return .success(.zero)
}
  • viewController

其闭包的返回值规定为 ViewControllerResultResult<UIViewController, RouterError>):

Router<Global>.register(for: "your router") { (url, value) -> ViewControllerResult in
    return .success(UIViewController()) // your controller
}

对于在 封装 一节中编写的的示例,其对应的注册代码如下所示:

// Need to declare a class and follow the `RouterRegister` protocol
private class ModuleARegister: RouterRegister {
    
    static func register() {
        
        let router = Router<ModuleA>.self
        
        router.register(for: .doSomething) { (url, value) -> DoResult in
            
            guard let param = value as? (start: Date, end: Date) else {
                return .failure(.parameterError(url: url, parameter: value))
            }
            
            print("We are doing these things from \(param.start) to \(param.end)")
            return .success(())
        }
        
        router.register(for: .calculateFrame) { (url, value) -> GetResult<AnyResult> in
            
            guard let screenWidth = value as? CGFloat else {
                return .failure(.parameterError(url: url, parameter: value))
            }
            
            return .success(CGRect(x: 0, y: 0, width: screenWidth * 0.25, height: screenWidth))
        }
        
        router.register(for: .create) { (url, value) -> ViewControllerResult in
            return .success(UIViewController())
        }
    }
}

最后的最后,需要在 AppDelegate.swiftapplication(_:, didFinishLaunchingWithOptions:) 方法中执行注册代码:

// In AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // some codes with higher priority than registered routes

    // initialize modules
    Router<Modules>.initialize()

    // some other code ..
}

中文


English

Clone this wiki locally