-
Notifications
You must be signed in to change notification settings - Fork 0
Quick start
The following content will tell you how to use the library, but it basically does not involve "Why do you do this" and "What does this type mean"
See other wikis for more details. For complete code examples, please refer to the demo provided with the warehouse (under the
Examples
directory).
RaRouter
provides the following three routing operations by default:
-
do
: execute certain operations:
let router = "rakuyo://moduleA/do/something"
_ = Router<Global>.do(router, param: ("parameter", 1))
-
get
: execute some operation and get its return value:
let router = "rakuyo://moduleA/calculate/frame"
let result = Router<Global>.get(of: String.self, from: router, param: "parameter")
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: "parameter").get(default: "defaultString")
// When an error occurs, "defaultString" will be printed
print(string)
-
viewController
: execute certain operations and get the returnedUIViewController
subclass:
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)
}
Some explanations:
-
The
Router
type is a default type provided byRaRouter
that complies with theRaRouter
protocol and is used to execute and register routes. Please refer to its statement: Router. -
For the contents of the
RaRouter
protocol, please refer to the wiki or the demo provided with the warehouse. -
The
Router
type requires a generic type, which represents the module to which the route belongs. TheGlobal
generic type stands for "global module". Related concepts and content will be introduced in detail in Advanced Tutorial. -
For the
param
parameter, it is of typeAny?
, And the default isnil
. This means that you can pass any parameters you want without any restrictions.For example, in the
do
example above, a tuple of type(String, Int)
was passed as a parameter, while in theget
example only aString
was passed. InviewController
, because"rakuyo://moduleA/create"
does not require any parameters (if the code is written), so you can omit theparam
parameter directly. -
The
viewController
operation is not like theget
operation, and thetype
parameter is added to the method to convert the type.Because the definition of
Controller
is often in the core component, andRoRouter
requires the routing component to be independent and not dependent on other components as much as possible. So under normal circumstances, when calling theviewController
method, the user cannot get the real type ofViewController
. When performing theget
operation, because we can define the model class in the routing component, we designed thetype
parameter and provided related errors when the conversion failed.
We can also optimize the above code, for example, encapsulate router
:
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"
}
}
The above code defines a module ModuleA
, which encapsulates the "function" it contains.
Then you can also encapsulate the "operation":
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)
}
}
Now, when we execute the test code in the Execute route section, we can write:
// 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() { }
Finally, we need to register the route we just defined.
For the three operations provided by default, each operation has a different registration method. The difference between them lies in the return value of the closure:
do
The return value of its closure is specified as DoResult
( Result<Void, RouterError>
):
Router<Global>.register(for: "your router") { (url, value) -> DoResult in
// do something
return .success(())
}
get
The return value of its closure is specified as GetResult<T>
(Result<T, RouterError>
):
You can expand it to any type you need that is not UIViewController
and Void
, such as CGRect
:
Router<Global>.register(for: "your router") { (url, value) -> GetResult<CGRect> in
return .success(.zero) // Anything you need
}
viewController
The return value of its closure is specified as ViewControllerResult
( Result<UIViewController, RouterError>
):
Router<Global>.register(for: "your router") { (url, value) -> ViewControllerResult in
return .success(UIViewController()) // your controller
}
For the example in the Encapsulation section, we should write the registration code
corresponding to ModuleA
as follows:
// Following the `RouterRegister` protocol is the key
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<CGRect> 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())
}
}
}
At the end, the registration code is executed in the application (_ :, didFinishLaunchingWithOptions:)
method of 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 ..
}