SwiftTypedRouter 0.1.1

SwiftTypedRouter 0.1.1

Sam Dean 维护。



  • 作者
  • deanWombourne

SwiftTypedRouter

为 SwiftUI 提供的强类型路由

CI Version License Platform

库的目标

  • Swift是强类型。我们能把路由库做到多强类型化?
  • 单一位置用于自定义路由
  • 即使运行代码的是另一个库(即如果我们有一些共享代码,我们应该能够配置其流程),路由也应该是可自定义的

安装

这是一个 Cocoapod。将 pod 'SwiftTypedRouter' 添加到您的 podfile 中。更多信息请见 https://cocoapods.org.cn。查看本存储库中的示例应用,了解它作为 pod 的实际应用。

您也可以简单地查看它并复制文件到您的应用中,如果喜欢的话。

Carthage / SPM 的支持是 issues - 欢迎获取它们并提交 PR ;)

基本使用

设置

启动时(可能是在你的 SceneDelegate 中)使用 Router 注册路由

let router = Router()

router.add("home") { HomeView() }

router.add("product/list/:cat/:num") { (category: String, page: Int) in
  ProductListView(category, page)
}

router.add("product/details/:id") { (id: String) in 
  ProductDetailsView(id: id)
}

并将路由实例提供给视图使用

yourFirstView
  .withRouter(router)

使用

给定一个路径,如果匹配不到,则路由器将返回一个视图(或找不到匹配项目时,将返回自己的404视图)

  NavigationLink(destination: router.view("product/list/hats/0")) {
    Text("See a list of Hats")
  }

您还可以在路由器匹配路径之前先检查是否匹配。注意:这比检查 view(_:) != nil 更快

if router.canMatch("product/list/hats/0") {
    NavigationLink(destination: router.view("product/list/hats/0")) {
      Text("See a list of Hats")
    }
} else {
    Text("No Hats For You")
}

高级用法

基本用法传递很多字符串。这没问题,但您可以使用更强类型化的值集使用该路由器。

模板

在幕后,调用 router.add("product/details/:id") 是使用所传递的路径和操作块的参数创建一个 Template 类型的操作。您可以使用 TemplateFactory 创建一个 Template

extension Template {
  static let productDetails = TemplateFactory.start().path("product", "details").placeholder("id", String.self).template()
}

当向路由器添加操作时,您可以使用它。

// Old
// router.add("product/details/:id") { (id: String) in 

// With template
router.add(Template.productDetails) { id in
  ProductDetailsView(id: id)
}

到目前为止,这并没有太多的好处。

您还可以使用模板来创建传递给路由器的路径 - 这些将是类型安全的。

extension Path {
  static func productDetails(id: String) -> Path { Template.productDetails.path(id) }
}

现在,当您使用路由器查找视图时,您会这样做

  // Old
  // NavigationLink(destination: router.view("product/details/123456")) {

  NavigationLink(destination: router.view(.productDetails(id: "123456"))) {
    Text("See Product Details")
  }

设置起来稍微有些复杂,但您只需键入一次路径,编译器就会为您执行所有的合理性错误检查。

别名

有时,你想在更细粒度的层面上配置你的应用程序,即你希望product/details/123导航到产品详情页面,但你想覆盖特定UI元素的目标位置。

如果你有访问源视图代码的权限,那么这很简单——只需编辑文件即可。然而,如果你的视图位于库中,你可能希望配置多个应用程序具有不同的行为。别名允许你这样做。

别名将一个路径重定向到另一个路径,并具有更丰富的上下文。

例如,如果我们在含有产品列表的屏幕上有一个+按钮,我们会这样做:

  1. (可选) 创建一个上下文类型(如果你想,可以是Void
struct ProductListAliasContext {
  let category: MyCategoryType
}
  1. 创建一个别名——它的标识符可以是任何你想要的,但使其唯一
let productListPlusTapAlias = Alias<ProductListAliasContext>("product.list.plus.tap")
  1. 配置你的路由器将别名重定向到正确的路径
router.alias(productListPlusTapAlias) { context in
  if context.category == "hats" {
    return Path.specialHatHandlingView
  } else {
    return Path.normalProductAddView
  }
}
  1. 在视图中直接使用别名而不是路径
  var context: ProductListAliasContext { ProductListAliasContext(category: self.categpory) }
  ...
  NavigationLink(destination: router.view(productListPlusTapAlias, context: context))) {
    Text("+")
  }

NB 如果你不想使用上下文,定义你的别名为Alias<Void>("some.identifier"),你不需要传递上下文参数。