甘道夫 0.2.0

甘道夫 0.2.0

limboy维护。



甘道夫 0.2.0

甘道夫

Swift Cocoapods

甘道夫是一个轻量级的纯Swift路由器,用于URL处理。让进入的URL找到合适的处理器。这就是甘道夫所能做的。

Gandalf Workflow

那么处理器是什么意思呢?

protocol Handler: class {
    func convert(params:Dictionary<String, String>, queryParams: Dictionary<String, String>)
    func handle()
}

如果甘道夫找到了正确的处理器,它将首先带着`params`和`queryParams`调用`convert`方法。在这个方法中,这些params可以被转换成内部属性。

接下来会调用handle()。您可以在其中做所有的事情。比如构建一个视图控制器,将其推送或显示,或者基于传入的params进行切换,或者弹出警告窗口等。

入门

首先让我们创建一些处理器。

class ProfileHandler:Handler {
	var profileID: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		profileID = queryParams["id"]
	}
	
	func handle() {
		if let id = profileID {
			let vc = ProfileViewController()
			vc.profileID = id
			navigationController.push(vc)
		} else {
			// present error page
		}
	}
}

class UserHandler:Handler {
	var username: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		username = params["username"]
	}
	
	func handle() {
		if let id = username {
			let vc = UserFollowingViewController()
			navigationController.push(vc)
		}
	}
}

然后使用适当的URL模式将这些处理器连接起来。

let routeTable:[String:Handler] = [
	"app://profile": ProfileHandler(),
	"app://user/{username}/following": UserHandler(),
]

Gandalf.instance.registerRoutingTable(routeTable, notFoundHandler: {url in print("\(url) not found")})

现在让我们处理URL!

// for test case
Gandalf.instance.handleURL(url: "app://profile?id=1024")
Gandalf.instance.handleURL(url: "app://user/limboy/following")

// usually it should be put in `AppDelegate's` method
func application(_ application: UIApplication,
                   open url: URL,
                   sourceApplication: String?,
                   annotation: Any) -> Bool {

	// ...
	Gandalf.instance.handleURL(url: url.absoluteString)
  }

如果甘道夫在注册表中找到了URL的模式,目标处理器将触发,否则将调用notFoundHandler,在那里可以显示一个自定义的视图控制器。

安装

Podfile

pod 'gandalf'

场景

推送一个viewController

例如,点击 ShopItemListViewController 中的某一项,应推送一个 DetailViewController。可以这样实现:

let handler = DetailHandler()
handler.id = 1024
handler.handle()

DetailHandler 作为 DetailViewController 的入口/包装器,无论它是从URL传入还是内部调用,都统一处理。

因此,您不需要打开目标 ViewController,试图找出需要初始化的内容。它对 DetailViewController 来说也很有弹性,即使某些属性应该在 DetailHandler 未受影响的情况下更改。

插画风格

当Pinterest的瀑布流列表项被点击时,其模型将被传递到详情页,因此它不会一开始就是空的。但是当通过URL打开时,不会传递任何模型。这些两种情况都由 DetailHandler 处理。

class DetailHandler:Handler {
	var itemModel: ItemModel?
	var itemID: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		itemID = params["id"]
	}
	
	func handle() {
		// opened internal
		if let model = itemModel {
			let vc = DetailViewController()
			vc.model = itemModel
			navigationController.push(vc)
			return
		}

		// opened by url
		if let id = itemID {
			let vc = DetailViewController()
			vc.id = itemID
			navigationController.push(vc)
		}
	}
}

当基本模型填写完成或用户进行了某些更改时,前一个 ViewController 应该知道并相应更新。只需添加一个像 onModelUpdate() 这样的属性,当模型更新时只需调用此函数(如果它不为nil)。

路由设计

URL模式存储在一个类似树的radix中,它会首先将传入的url分割成多个部分,然后通过树逐一匹配它们。

技巧

  • 为了简化事物,URL模式仅匹配到String。例如,app://profile/{id}将使convert方法中获取的params["id"]作为String
  • 可以将导航器注入到Handler的子协议中,以使其更易于导航。
  • 当通过类似?debugcode=xxx的URL打开时,可以使用SomeHandler作为配置处理器,开启调试模式。

联系

您可以在Twitter上关注并与我联系。如果您发现任何问题,请打开一个工单。拉取请求也热烈欢迎。

许可

本项目采用MIT许可证发布。具体信息请参见LICENSE文件。