美文网首页Swift
Swift URLNavigator 框架

Swift URLNavigator 框架

作者: fuyoufang | 来源:发表于2019-11-07 14:46 被阅读0次

    ⛵️ URLNavigator 是 Swift 下一个优雅的 URL 路由。它提供了通过 URL 导航到 view controller 的方式。URL 参数的对应关系通过 URLNavigator.register(_:_:) 方法进行设置。

    URLNavigator 提供了两种方法来设置 URL 参数的对应关系:URLNavigableURLOpenHandlerURLNavigable 通过自定义的初始化方法进行设置,URLOpenHandler 通过一个可执行的闭包进行设置。初始化方法和闭包都接受一个 URL 和占位符值。

    开始

    1. 理解 URL 模式

    URL 模式可以包含多个占位符。占位符将会被匹配的 URL 中的值替换。使用 <> 来设置占位符。占位符的类型可以设置为:string(默认), int, float, 和 path

    例如,myapp://user/<int:id> 将会和下面的 URL 匹配:

    • myapp://user/123
    • myapp://user/87

    但是,无法和下面的 URL 配置:

    • myapp://user/devxoul (类型错误,需要 int)
    • myapp://user/123/posts (url 的结构不匹配))
    • /user/devxoul (丢失 scheme)

    2. View Controllers 和 URL 打开操作的匹配

    URLNavigator 通过 URL 模式来匹配 view controllers 和 URL 的打开操作。下面是使用 view controller 和闭包映射 URL 模式的示例。每个闭包有三个参数:url, valuescontext

    • url 是通过 push() 或者 present() 传递的 URL 参数.
    • values 是一个包含 URL 占位符的 keys 和 values 的字典.
    • context 是通过 push(), present()open() 传递的额外值的字典。
    let navigator = Navigator()
    
    // register view controllers
    navigator.register("myapp://user/<int:id>") { url, values, context in
      guard let userID = values["id"] as? Int else { return nil }
      return UserViewController(userID: userID)
    }
    navigator.register("myapp://post/<title>") { url, values, context in
      return storyboard.instantiateViewController(withIdentifier: "PostViewController")
    }
    
    // register url open handlers
    navigator.handle("myapp://alert") { url, values, context in
      let title = url.queryParameters["title"]
      let message = url.queryParameters["message"]
      presentAlertController(title: title, message: message)
      return true
    }
    

    3. 弹出方式(Pushing, Presenting)与操作 URLs

    URLNavigator 可以通过执行一个带 URLs 参数的闭包来 push 或者 presenet 对应的 view controllers。

    push() 方法中,通过指定 from 参数可以设置弹出新 view controller 的 navigation controller。同样,在 present() 方法中,通过指定 from 参数可以设置弹出新 view controller 的 view controller。如果设置为 nil,也就是默认值,当前应用程序最顶层的 view controller 将会被用来 push 或 present 新的 view controller。

    present() 还有一个额外的参数 wrap。如果将这个参数设置为 UINavigationController 类型,则会用这种类型的导航控制器把要弹出的新的 view controller 包住。这个参数的默认值为 nil

    Navigator.push("myapp://user/123")
    Navigator.present("myapp://post/54321", wrap: UINavigationController.self)
    
    Navigator.open("myapp://alert?title=Hello&message=World")
    

    安装

    官方仅提供了通过 CocoaPods 安装的方式。

    Podfile

    pod 'URLNavigator'
    

    示例

    示例程序 点击查看.

    1. 编译并安装示例 app。
    2. 打开 Safari app。
    3. 在 URL 栏输入 navigator://user/devxoul
    4. 示例程序将会被加载。

    提示和窍门

    初始化 Navigator 实例的时机

    1. 定义一个全局的常量:

      let navigator = Navigator()
      
      class AppDelegate: UIResponder, UIApplicationDelegate {
        // ...
      }
      
    2. 向 IoC 容器内注册

      container.register(NavigatorType.self) { _ in Navigator() } // Swinject
      let navigator = container.resolve(NavigatorType.self)!
      
    3. 在合成根部注册依赖

    Inject dependency from a composition root.

    关联 URL 的时机

    作者喜欢将 URL 映射关系放到单独的文件中。

    struct URLNavigationMap {
      static func initialize(navigator: NavigatorType) {
        navigator.register("myapp://user/<int:id>") { ... }
        navigator.register("myapp://post/<title>") { ... }
        navigator.handle("myapp://alert") { ... }
      }
    }
    

    然后在 AppDelegateapplication:didFinishLaunchingWithOptions: 方法中调用 initialize()

    @UIApplicationMain
    final class AppDelegate: UIResponder, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
      ) -> Bool {
        // Navigator
        URLNavigationMap.initialize(navigator: navigator)
        
        // Do something else...
      }
    }
    

    实现 AppDelegate 启动选项 URL

    如果注册了自定义的 schemes,就可以通过 URLs 来启动我们的 app 了。可以通过实现 application:didFinishLaunchingWithOptions: 方法来实现通过 URLs 导航到指定的 view controller。

    func application(
      _ application: UIApplication,
      didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {
      // ...
      if let url = launchOptions?[.url] as? URL {
        if let opened = navigator.open(url)
        if !opened {
          navigator.present(url)
        }
      }
      return true
    }
    
    

    实现 AppDelegate 打开 URL 的方法

    你可能需要实现自定义的 URL 打开处理程序。下面是一个使用 URLNavigator 和其他 URL 打开处理程序的示例:

    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
      // If you're using Facebook SDK
      let fb = FBSDKApplicationDelegate.sharedInstance()
      if fb.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation) {
        return true
      }
    
      // URLNavigator Handler
      if navigator.open(url) {
        return true
      }
    
      // URLNavigator View Controller
      if navigator.present(url, wrap: UINavigationController.self) != nil {
        return true
      }
    
      return false
    }
    

    在 Pushing, Presenting 或 Opening 时传入附加值

    let context: [AnyHashable: Any] = [
      "fromViewController": self
    ]
    Navigator.push("myapp://user/10", context: context)
    Navigator.present("myapp://user/10", context: context)
    Navigator.open("myapp://alert?title=Hi", context: context)
    

    定义自定义 URL 值的转换器

    你可以自定义给 URL 占位符设置 URL 值的转化器。

    例如,占位符 <region> 仅能够接受 ["us-west-1", "ap-northeast-2", "eu-west-3"] 中的字符串。如果传入的参数不包含在这些值中,URL 的参数将无法匹配。

    下面是在 Navigator 实例的 [String: URLValueConverter] 字典中添加一个自定义的值转换器。

    navigator.matcher.valueConverters["region"] = { pathComponents, index in
      let allowedRegions = ["us-west-1", "ap-northeast-2", "eu-west-3"]
      if allowedRegions.contains(pathComponents[index]) {
        return pathComponents[index]
      } else {
        return nil
      }
    }
    

    通过上面的代码,myapp://region/<region:_> 将会匹配下面的 URL:

    • myapp://region/us-west-1
    • myapp://region/ap-northeast-2
    • myapp://region/eu-west-3

    但是和下面的 URL 就无法匹配了:

    • myapp://region/ca-central-1

    其他相关信息,可以查看 URL 值的默认转换器的实现方式

    相关文章

      网友评论

        本文标题:Swift URLNavigator 框架

        本文链接:https://www.haomeiwen.com/subject/fukvbctx.html