Carthage compatible Platform support

一个用于 ReSwift 的声明式路由。 允许开发者以类似于在 Web 上使用 URL 的方式声明路由。

使用 ReSwiftRouter,您可以通过定义 URL 样式的标识符序列来导航您的应用程序。

mainStore.dispatch(
    SetRouteAction(["TabBarViewController", StatsViewController.identifier])
)

关于 ReSwiftRouter

ReSwiftRouter 仍在开发中,API 在目前阶段既不完整也不稳定。

在使用 ReSwift 构建应用程序时,您应该旨在通过 actions 引起所有状态更改 - 这包括对导航状态的更改。

这需要将当前的导航状态存储在应用程序状态中,并使用 actions 来触发对该状态的更改 - ReSwiftRouter 提供了这两者。

安装

CocoaPods

您可以通过将 ReSwiftRouter 添加到您的 Podfile 来使用 CocoaPods 安装它

use_frameworks!

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

pod 'ReSwift'
pod 'ReSwiftRouter'

并运行 pod install

Carthage

您可以通过将以下行添加到您的 Cartfile 中,使用 Carthage 安装 ReSwiftRouter

github "ReSwift/ReSwift-Router"

配置

扩展您的应用程序状态以包含导航状态

import ReSwiftRouter

struct AppState: StateType {
    // other application state
    var navigationState: NavigationState
}

在初始化您的 store 后,创建一个 Router 的实例,传入对 store 和根 Routable 的引用。 此外,您需要提供一个闭包,描述如何访问应用程序状态的 navigationState

router = Router(store: mainStore, rootRoutable: RootRoutable(routable: rootViewController)) { state in 
	state.select { $0.navigationState }
}

我们将在下一个主要章节中讨论 Routable

调用 Navigation Reducer

NavigationReducer 作为 ReSwiftRouter 的一部分提供。 您需要在顶级 reducer 中调用它。 这是一个来自规范的简单示例

struct AppReducer: Reducer {
    func handleAction(action: Action, state: FakeAppState?) -> FakeAppState {
        return FakeAppState(
            navigationState: NavigationReducer.handleAction(action, state: state?.navigationState)
        )
    }
}

这将使 reducer 处理所有与路由相关的 actions。

实现 Routable

ReSwiftRouter 使用以类似于 URL 的方式定义的路由,作为一个元素序列,例如 ["Home", "User", "UserDetail"]

ReSwiftRouter 与您使用的 UI 框架无关 - 它使用 Routable 来实现这种交互。

每个路由元素都映射到一个负责的 RoutableRoutable 需要能够呈现一个子视图,隐藏一个子视图,或用另一个子视图替换一个子视图。

这是 Routable 协议,其中包含您应该实现的方法

public protocol Routable {

    func push(
        _ element: RouteElement,
        animated: Bool,
        completion: @escaping RoutingCompletion) -> Routable

    func pop(
        _ element: RouteElement,
        animated: Bool,
        completion: @escaping RoutingCompletion)

    func change(
        _ from: RouteElement,
        to: RouteElement,
        animated: Bool,
        completion: @escaping RoutingCompletion) -> Routable

}

作为初始化 Router 的一部分,您需要将第一个 Routable 作为参数传递。 根 Routable 将负责第一个路由元素。

例如,如果您将应用程序的路由设置为 ["Home"],则将要求您的根 Routable 呈现与元素 "Home" 相对应的视图。

如果在 iOS 上使用 UIKit,这将意味着 Routable 需要设置应用程序的 rootViewController

每当 Routable 呈现一个新的路由元素时,它需要返回一个新的 Routable,它将负责管理呈现的元素。 如果您想从 ["Home"] 导航到 ["Home", "Users"],则将要求负责 "Home" 元素的 Routable 呈现 "User" 元素。

如果您的导航堆栈使用模态演示进行此转换,则 "Home" 元素的 Routable 实现可能如下所示

func push(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion) -> Routable {

	if element == "User" {
		// 1.) Perform the transition
        userViewController = UIStoryboard(name: "Main", bundle: nil)
            .instantiateViewControllerWithIdentifier("UserViewController") as! Routable

		// 2.) Call the `completion` once the transition is complete
        presentViewController(userViewController, animated: false,
            completion: completion)

		// 3.) Return the Routable for the presented element. For convenience
		// this will often be the UIViewController itself. 
        return userViewController
   	}
   	
   	// ...
}

func pop(_ element: RouteElement, animated: Bool, completion: @escaping RoutingCompletion)

	if element == "Home" {
    	dismissViewControllerAnimated(false, completion: completion)
    }
    
    // ...
}

在 Routables 中调用 Completion Handler

ReSwiftRouter 需要限制导航 actions,因为包括 UIKit 在内的许多 UI 框架不允许并行执行多个导航步骤。 因此,Routable 的每个方法都会收到一个 completion handler。 在调用 completion handler 之前,路由器不会执行任何进一步的导航 actions。

更改当前路由

目前,更改当前应用程序路由的唯一方法是使用 SetRouteAction 并提供绝对路由。 这是一个简短的例子

@IBAction func cancelButtonTapped(sender: UIButton) {
    mainStore.dispatch(
        SetRouteAction(["TabBarViewController", StatsViewController.identifier])
    )
}

随着开发的继续,将添加对更改单个路由元素的支持。

贡献

编译 & 运行测试

ReSwiftRouter 使用 Carthage 作为其开发依赖项。 要构建或测试任何目标,请运行 carthage bootstrap