{"id":15025247,"url":"https://github.com/devxoul/urlnavigator","last_synced_at":"2025-05-14T12:09:49.380Z","repository":{"id":37731197,"uuid":"51241838","full_name":"devxoul/URLNavigator","owner":"devxoul","description":"⛵️ Elegant URL Routing for Swift","archived":false,"fork":false,"pushed_at":"2024-05-24T16:28:03.000Z","size":310,"stargazers_count":3259,"open_issues_count":44,"forks_count":297,"subscribers_count":53,"default_branch":"master","last_synced_at":"2024-11-24T20:40:05.027Z","etag":null,"topics":["deeplink","ios","routing","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devxoul.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-07T09:14:17.000Z","updated_at":"2024-11-23T10:15:00.000Z","dependencies_parsed_at":"2022-07-13T03:50:37.373Z","dependency_job_id":"0834ef21-4cb5-40fe-a11b-5becdedbe63e","html_url":"https://github.com/devxoul/URLNavigator","commit_stats":{"total_commits":239,"total_committers":15,"mean_commits":"15.933333333333334","dds":0.2259414225941423,"last_synced_commit":"00bd578c30e9fcbcf1400f742dfa5a2e8050f16c"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devxoul%2FURLNavigator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devxoul%2FURLNavigator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devxoul%2FURLNavigator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devxoul%2FURLNavigator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devxoul","download_url":"https://codeload.github.com/devxoul/URLNavigator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247968370,"owners_count":21025823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deeplink","ios","routing","swift"],"created_at":"2024-09-24T20:01:53.458Z","updated_at":"2025-04-09T03:10:49.419Z","avatar_url":"https://github.com/devxoul.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# URLNavigator\n\n![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)\n[![CocoaPods](http://img.shields.io/cocoapods/v/URLNavigator.svg)](https://cocoapods.org/pods/URLNavigator)\n[![Build Status](https://github.com/devxoul/URLNavigator/workflows/CI/badge.svg)](https://github.com/devxoul/URLNavigator/actions)\n[![CodeCov](https://img.shields.io/codecov/c/github/devxoul/URLNavigator.svg)](https://codecov.io/gh/devxoul/URLNavigator)\n\n⛵️ URLNavigator provides an elegant way to navigate through view controllers by URLs. URL patterns can be mapped by using `URLNavigator.register(_:_:)` function.\n\nURLNavigator can be used for mapping URL patterns with 2 kind of types: `URLNavigable` and `URLOpenHandler`. `URLNavigable` is a type which defines an custom initializer and `URLOpenHandler` is a closure which can be executed. Both an initializer and a closure receive an URL and placeholder values.\n\n\n## Getting Started\n\n#### 1. Understanding URL Patterns\n\nURL patterns can contain placeholders. Placeholders will be replaced with matching values from URLs. Use `\u003c` and `\u003e` to make placeholders. Placeholders can have types: `string`(default), `int`, `float`, and `path`.\n\nFor example, `myapp://user/\u003cint:id\u003e` matches with:\n\n* `myapp://user/123`\n* `myapp://user/87`\n\nBut it doesn't match with:\n\n* `myapp://user/devxoul` (expected int)\n* `myapp://user/123/posts` (different url structure)\n* `/user/devxoul` (missing scheme)\n\n#### 2. Mapping View Controllers and URL Open Handlers\n\nURLNavigator allows to map view controllers and URL open handlers with URL patterns. Here's an example of mapping URL patterns with view controllers and a closure. Each closures has three parameters: `url`, `values` and `context`.\n\n* `url` is an URL that is passed from `push()` and `present()`.\n* `values` is a dictionary that contains URL placeholder keys and values.\n* `context` is a dictionary which contains extra values passed from `push()`, `present()` or `open()`.\n\n```swift\nlet navigator = Navigator()\n\n// register view controllers\nnavigator.register(\"myapp://user/\u003cint:id\u003e\") { url, values, context in\n  guard let userID = values[\"id\"] as? Int else { return nil }\n  return UserViewController(userID: userID)\n}\nnavigator.register(\"myapp://post/\u003ctitle\u003e\") { url, values, context in\n  return storyboard.instantiateViewController(withIdentifier: \"PostViewController\")\n}\n\n// register url open handlers\nnavigator.handle(\"myapp://alert\") { url, values, context in\n  let title = url.queryParameters[\"title\"]\n  let message = url.queryParameters[\"message\"]\n  presentAlertController(title: title, message: message)\n  return true\n}\n```\n\n#### 3. Pushing, Presenting and Opening URLs\n\nURLNavigator can push and present view controllers and execute closures with URLs.\n\nProvide the `from` parameter to `push()` to specify the navigation controller which the new view controller will be pushed. Similarly, provide the `from` parameter to `present()` to specify the view controller which the new view controller will be presented. If the `nil` is passed, which is a default value, current application's top most view controller will be used to push or present view controllers.\n\n`present()` takes an extra parameter: `wrap`. If a `UINavigationController` class is specified, the new view controller will be wrapped with the class. Default value is `nil`.\n\n```swift\nNavigator.push(\"myapp://user/123\")\nNavigator.present(\"myapp://post/54321\", wrap: UINavigationController.self)\n\nNavigator.open(\"myapp://alert?title=Hello\u0026message=World\")\n```\n\n\n## Installation\n\nURLNavigator officially supports CocoaPods only.\n\n**Podfile**\n\n```ruby\npod 'URLNavigator'\n```\n\n\n## Example\n\nYou can find an example app [here](https://github.com/devxoul/URLNavigator/tree/master/Example).\n\n1. Build and install the example app.\n2. Open Safari app\n3. Enter `navigator://user/devxoul` in the URL bar.\n4. The example app will be launched.\n\n\n## Tips and Tricks\n\n#### Where to initialize a Navigator instance\n\n1. Define as a global constant:\n\n    ```swift\n    let navigator = Navigator()\n\n    class AppDelegate: UIResponder, UIApplicationDelegate {\n      // ...\n    }\n    ```\n\n2. Register to an IoC container:\n\n    ```swift\n    container.register(NavigatorProtocol.self) { _ in Navigator() } // Swinject\n    let navigator = container.resolve(NavigatorProtocol.self)!\n    ```\n\n3. Inject dependency from a composition root.\n\n\n#### Where to Map URLs\n\nI'd prefer using separated URL map file.\n\n```swift\nstruct URLNavigationMap {\n  static func initialize(navigator: NavigatorProtocol) {\n    navigator.register(\"myapp://user/\u003cint:id\u003e\") { ... }\n    navigator.register(\"myapp://post/\u003ctitle\u003e\") { ... }\n    navigator.handle(\"myapp://alert\") { ... }\n  }\n}\n```\n\nThen call `initialize()` at `AppDelegate`'s `application:didFinishLaunchingWithOptions:`.\n\n```swift\n@UIApplicationMain\nfinal class AppDelegate: UIResponder, UIApplicationDelegate {\n  func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?\n  ) -\u003e Bool {\n    // Navigator\n    URLNavigationMap.initialize(navigator: navigator)\n    \n    // Do something else...\n  }\n}\n```\n\n\n#### Implementing AppDelegate Launch Option URL\n\nIt's available to open your app with URLs if custom schemes are registered. In order to navigate to view controllers with URLs, you'll have to implement `application:didFinishLaunchingWithOptions:` method.\n\n```swift\nfunc application(\n  _ application: UIApplication,\n  didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?\n) -\u003e Bool {\n  // ...\n  if let url = launchOptions?[.url] as? URL {\n    if let opened = navigator.open(url)\n    if !opened {\n      navigator.present(url)\n    }\n  }\n  return true\n}\n\n```\n\n\n#### Implementing AppDelegate Open URL Method\n\nYou'll might want to implement custom URL open handler. Here's an example of using URLNavigator with other URL open handlers.\n\n```swift\nfunc application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -\u003e Bool {\n  // If you're using Facebook SDK\n  let fb = FBSDKApplicationDelegate.sharedInstance()\n  if fb.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation) {\n    return true\n  }\n\n  // URLNavigator Handler\n  if navigator.open(url) {\n    return true\n  }\n\n  // URLNavigator View Controller\n  if navigator.present(url, wrap: UINavigationController.self) != nil {\n    return true\n  }\n\n  return false\n}\n```\n\n\n#### Passing Extra Values when Pushing, Presenting and Opening\n\n```swift\nlet context: [AnyHashable: Any] = [\n  \"fromViewController\": self\n]\nNavigator.push(\"myapp://user/10\", context: context)\nNavigator.present(\"myapp://user/10\", context: context)\nNavigator.open(\"myapp://alert?title=Hi\", context: context)\n```\n\n\n#### Defining custom URL Value Converters\n\nYou can define custom URL Value Converters for URL placeholders.\n\nFor example, the placeholder `\u003cregion\u003e` is only allowed for the strings `[\"us-west-1\", \"ap-northeast-2\", \"eu-west-3\"]`. If it doesn't contain any of these, the URL pattern should not match.\n\nAdd a custom value converter to the `[String: URLValueConverter]` dictionary on your instance of `Navigator`.\n\n```swift\nnavigator.matcher.valueConverters[\"region\"] = { pathComponents, index in\n  let allowedRegions = [\"us-west-1\", \"ap-northeast-2\", \"eu-west-3\"]\n  if allowedRegions.contains(pathComponents[index]) {\n    return pathComponents[index]\n  } else {\n    return nil\n  }\n}\n```\n\nWith the code above, for example, `myapp://region/\u003cregion:_\u003e` matches with:\n- `myapp://region/us-west-1`\n- `myapp://region/ap-northeast-2`\n- `myapp://region/eu-west-3`\n\nBut it doesn't match with:\n- `myapp://region/ca-central-1`\n\nFor additional information, see the [implementation](https://github.com/devxoul/URLNavigator/blob/master/Sources/URLMatcher/URLMatcher.swift) of default URL Value Converters.\n\n\n## License\n\nURLNavigator is under MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevxoul%2Furlnavigator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevxoul%2Furlnavigator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevxoul%2Furlnavigator/lists"}