{"id":21539322,"url":"https://github.com/nextdoor/corridor","last_synced_at":"2025-04-10T03:26:03.703Z","repository":{"id":32686817,"uuid":"131195081","full_name":"Nextdoor/corridor","owner":"Nextdoor","description":"Corridor lets you easily match URLs and extract their values","archived":false,"fork":false,"pushed_at":"2022-05-04T18:43:17.000Z","size":1316,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":34,"default_branch":"master","last_synced_at":"2024-04-14T09:58:15.144Z","etag":null,"topics":["codable","deeplinks","ios","swift","universal-links"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nextdoor.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}},"created_at":"2018-04-26T18:23:39.000Z","updated_at":"2022-09-20T00:41:54.000Z","dependencies_parsed_at":"2022-08-08T09:30:17.582Z","dependency_job_id":null,"html_url":"https://github.com/Nextdoor/corridor","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nextdoor%2Fcorridor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nextdoor%2Fcorridor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nextdoor%2Fcorridor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nextdoor%2Fcorridor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nextdoor","download_url":"https://codeload.github.com/Nextdoor/corridor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248149971,"owners_count":21055841,"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":["codable","deeplinks","ios","swift","universal-links"],"created_at":"2024-11-24T04:14:45.923Z","updated_at":"2025-04-10T03:26:03.672Z","avatar_url":"https://github.com/Nextdoor.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Corridor\n\n![Swift compatibility](https://img.shields.io/badge/Swift-4.0-orange.svg)\n\nSpend less time writing custom URL matching and parsing logic. Define any URL format you support using a single string, and let Corridor do the heavy lifting.\n\nSuppose you want to handle a user-profile universal-link like: `https://example.com/user/15127128`.\n\n(1) Create a *route* -- a [Codable](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types) struct that contains all the values you want to extract from the URL. Once the URL is matched, the userId will have a value of `15127128`\n```swift\nstruct ViewUserProfile: CorridorRoute {\n    let userId: Int\n}\n```\n\n(2) Register the format: `\"/user/:userId{int}\"`\n\n```swift\nrouter.register(\"/user/:userId{int}\", { try corridorResponse($0, ViewUserProfile.self) })\n```\n\n**Now, for the usage:**\n\n(3) Call `attemptMatch(_:URL)` with the url `https://example.com/user/15127128` to get a matched-response\n\n```swift\nlet result = router.attemptMatch(url)\n```\n\n(4) Switch on the result's `route`:\n\n```swift\nlet route = result.route\nswitch route {\n  case let route as ViewUserProfile:\n    print(route.userId)\n  default:\n    print(\"url not matched\")\n}\n```\n`15127128` *Voila!*\n\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Contribute](#contribute)\n- [License](#license)\n\n## Overview\n\nSee an example router that uses `Corridor` inside the `Example` folder\n\n### Route\n\nA struct that conforms to `CorridorRoute`. Its variables correspond to values contained in a URL. It's created when a URL is matched.\n\n### Router\n\nRouters facilitate the process of matching a URL to a route:\n\n```swift\nlet router = CorridorRouter()\n```\n\nLet's register a type of URL:\n\n```swift\nrouter.register(\"/user/:userId{int}\", { try corridorResponse($0, ViewUserProfile.self) })\n```\n\u003e ViewUserProfile is the struct that we defined in the first example\n\n`\"/user/:userId{int}\"` is the URL format. It matches URLs that start with `/user` and have an integer value after.\n\n`{ try corridorResponse($0, ViewUserProfile.self) }` is a block that populates the route `ViewUserProfile` if the URL is matched.\n\n### URL format\n\nA URL format is split into a path and a query:\n- Path: includes all the URL values separated by \"/\"\n- Query: all the key-value pairs after the URL's last \"/\"\n\n\u003e URL formats must start with a forward slash \"/\"\n\n#### Formatting the path\n\nSuppose you want to match URLs in the form `/user/12891`, that start with `user` and end with a number.\n\nThe URL format for this is: `\"/user/:userId{int}\"`, where the number after `user` is represented as `:userId{int}`.\n\nEach param you want to extract follows the form `:paramName{baseType}` or `:paramName`.\n- `paramName` is the same name as a variable in the route\n- `baseType` indicates the type of `paramName`\n\nSo `:userId` without an explicit base type would map to `let userId: String` in a route, while `:userId{int}` would map to `let userId: Int` in a route.\n\n\u003e URL definitions must start with a forward slash \"/\"\n\n\u003e Base type 'string' maps to a String\n\n\u003e Base type 'int' maps to a Int\n\n#### Formatting the query\n\nSuppose you want to match URLs in the form `/user?userId=12891`, that begin with a `user` path, and have a numeric `userId` value in the params.\n\nThe URL format for this is: `\"/user/?:userId{int}\"`, where everything after `?` is part of the query.\n\n**Optionals:**\n\nNow suppose that the `userId` value was optional, so `/user\u0026userId=12891` and `/user` would map to the same route.\n\nThe URL format for this is: `\"/user/?:userId{int?}\"`\n\n**Literals:**\n\nNow suppose that we wanted to match URLs in the form `/user\u0026userId=12891\u0026sourceType=email`, where the ('sourceType', 'email') pair was present in the query.\n\nThe URL format for this is: `\"/user/?:userId{int}\u0026:sourceType{'email'}\"`\n\n\u003e URL formats must separate consecutive query params with an ampersand '\u0026'\n\n### Support custom base types\n\nThe URL definition `\"/user/:userId{int}?:nickname{string}\"` contains built-in base types: `\"int\"`, `\"string\"`, and `\"bool\"`.\nEach base type maps to a Swift type:\n\n`\"int\" -\u003e Int`\n\n`\"string\" -\u003e String`\n\n`\"bool\" -\u003e Bool`\n\n\u003e Any base type can be enclosed by square brackets to represent a comma separated list of base values. Example: `\"[int]\" -\u003e [Int]`.\n\nWhat if you want to use a base type that isn't one of the built-in ones? Here's an example:\n\n(1) Define a base type named \"uint\" that converts values to UInt:\n\n```swift\nprivate struct CorridorTypeUInt: CorridorTypeProtocol {\n    let name = \"uint\"\n    func convertToType(from value: String) -\u003e Any? {\n      return UInt(value)\n    }\n}\n```\n\n\u003e Base type names must be alphabetic, and cannot be identical to any of the existing built-in base type names\n\n(2) Instantiate `CorridorTypes` with the custom base type:\n\n```swift\nlet baseTypes = CorridorTypes(customTypes: [CorridorTypeUInt()])\n```\n\n(3) Instantiate the router with the baseTypes\n\n```swift\nlet router = CorridorRouter(corridorTypes: baseTypes)\n```\n\n**Now, for the usage:**\n\n```swift\nstruct ViewUserProfile: CorridorRoute {\n    let userId: UInt\n    let nickname: String?\n}\n```\n\n```swift\nrouter.register(\"/user/:userId{uint}?:nickname{string}\", { try corridorResponse($0, ViewUserProfile.self) })\n```\n\n(4) Call `attemptMatch(_:URL)` with the url `www.example.com/8971/?nickname=Bob`\n\n```swift\nlet result = router.attemptMatch(url)\nlet route = routeResponse.route as! ViewUserProfile\nprint(route.userId)\n```\n`8971` *Voila!*\n\n\n### Match *global* query params\n\nWhat if a query parameter such as a tracking id, or user id is contained in many of your URLs?\n\nSuppose your URLs contained a query param `sourceUserId` to track the user who clicked on the URL.\n\nYou could include `let sourceUserId: String?` in all your routes, but doing so is tedious and error-prone. *CorridorGlobalParams* to the rescue!\n\n(1) Create a struct that conforms to `CorridorGlobalParams`\n\n```swift\nstruct GlobalParams: CorridorGlobalParams {\n  let sourceUserId: String?\n}\n```\n\n(2) Create a mapping\n\n```swift\nlet mapping = GlobalQueryOptionalParamsMapping(params: [\"sourceUserId\"],\n                                                       decoder: { try corridorGlobalParamsResponse($0, GlobalParams.self) })\n```\n\n(3) Instantiate the router with the mapping\n\n```swift\nlet router = CorridorRouter(globalQueryOptionalParamsMapping: mapping)\n```\n\n**Now, for the usage:**\n\n(4) Call `attemptMatch(_:URL)` with the url `www.example.com/news_feed/?sourceUserId=abc123`\n\n```swift\nlet result = router.attemptMatch(url)\nlet globalParams = routeResponse.globalParams as! GlobalParams\nprint(globalParams.sourceUserId)\n```\n`abc123` *Voila!*\n\n\n## Installation\nCorridor supports multiple methods for installing the library in a project.\n\n### CocoaPods\n\nTo integrate Corridor into your Xcode project using CocoaPods, specify it in your `Podfile`:\n\n```ruby\npod 'Corridor','~\u003e 1.0.3'\n```\n\nAlso include on a separate line in your `Podfile`:\n\n```ruby\nuse_frameworks!\n```\n\n### Carthage\n\nTo integrate Corridor into your Xcode project using Carthage, specify it in your `Cartfile`:\n\n```ogdl\ngithub \"Nextdoor/corridor\" ~\u003e 1.0.3'\n```\n\nRun `carthage` to build the framework and drag the built `Corridor.framework` into your Xcode project.\n\n### Swift Package Manager\n\nTo integrate Corridor into your Xcode project using Swift Package Manager, add it to the `dependencies` value of your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/Nextdoor/corridor.git\", from: \"1.0.3\")\n]\n```\n\n## Contribute\n\n- If you have a feature request or discover a bug, open an issue\n- If you'd like to contribute changes, submit a pull request\n\n## License\n\nCorridor is released under the Apache 2.0 license. [See LICENSE](https://github.com/Nextdoor/corridor/blob/master/LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextdoor%2Fcorridor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnextdoor%2Fcorridor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextdoor%2Fcorridor/lists"}