{"id":13872308,"url":"https://github.com/TizianoCoroneo/Deeplink","last_synced_at":"2025-07-16T02:30:36.879Z","repository":{"id":54602995,"uuid":"290524457","full_name":"TizianoCoroneo/Deeplink","owner":"TizianoCoroneo","description":"A library to parse deeplinks and their arguments using String interpolation.","archived":false,"fork":false,"pushed_at":"2024-06-15T14:28:14.000Z","size":1677,"stargazers_count":54,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-07T19:42:27.373Z","etag":null,"topics":["deep-links","deeplink-template","deeplinking","ios","swift"],"latest_commit_sha":null,"homepage":"https://tizianocoroneo.github.io/Deeplink/documentation/deeplink/","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/TizianoCoroneo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-08-26T14:49:21.000Z","updated_at":"2024-10-09T07:32:25.000Z","dependencies_parsed_at":"2024-02-02T14:51:24.515Z","dependency_job_id":null,"html_url":"https://github.com/TizianoCoroneo/Deeplink","commit_stats":{"total_commits":68,"total_committers":2,"mean_commits":34.0,"dds":"0.13235294117647056","last_synced_commit":"1c069246930732c25923a6bc59b0bd97ba8e44a3"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TizianoCoroneo%2FDeeplink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TizianoCoroneo%2FDeeplink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TizianoCoroneo%2FDeeplink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TizianoCoroneo%2FDeeplink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TizianoCoroneo","download_url":"https://codeload.github.com/TizianoCoroneo/Deeplink/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226095683,"owners_count":17572970,"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":["deep-links","deeplink-template","deeplinking","ios","swift"],"created_at":"2024-08-05T23:00:39.326Z","updated_at":"2024-11-23T20:30:54.912Z","avatar_url":"https://github.com/TizianoCoroneo.png","language":"Swift","funding_links":[],"categories":["Swift","iOS"],"sub_categories":["Deeplink"],"readme":"\n# 🎣 Deeplink\n\nA microlibrary to parse deeplinks and their arguments using Swift's String interpolation.\n\n\u003ca href=\"https://github.com/TizianoCoroneo/Deeplink/actions?query=workflow%3ATest\"\u003e\u003cimg alt=\"GitHub Actions status\" src=\"https://github.com/TizianoCoroneo/Deeplink/workflows/Test/badge.svg?branch=main\u0026event=push\"\u003e\u003c/a\u003e \u003cimg src=\"https://img.shields.io/badge/Swift-5.6-orange.svg\" /\u003e \u003ca href=\"https://swift.org/package-manager\"\u003e\u003cimg src=\"https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat\" alt=\"Swift Package Manager\" /\u003e\u003c/a\u003e \u003ca href=\"https://twitter.com/TizianoCoroneo\"\u003e\u003cimg src=\"https://img.shields.io/badge/contact-@TizianoCoroneo-blue.svg?style=flat\" alt=\"Twitter: @TizianoCoroneo\" /\u003e\u003c/a\u003e\n\n## [What is deeplinking?](https://branch.io/what-is-deep-linking/)\n\nMobile app's deep links (also known simply as “deep links”) are URLs that point to content inside an app. If you want to share a pair of shoes from Amazon with a friend, you can send a deep link that brings your friend directly to those shoes in the Amazon app. Without a deep link, your friend would have to find the Amazon app on the App Store or Play Store, open the app to the homepage, locate the Search function, and then try to find the same pair of shoes you found.\n\nThis package provides the tools to define a specific deeplink template, pattern match a URL with a deeplink template, and to parse the informations contained in the URL into an object. It does the parsing so you don't have to!\n\n## Installing using the Swift Package Manager\n\nThe library only supports the Swift Package Manager. \nTo install `Deeplink` for use in an app, command line tool or server-side application, add Deeplink as a dependency to your `Package.swift` file. For more information, please see the [Swift Package Manager documentation](https://github.com/apple/swift-package-manager/tree/master/Documentation).\n\n```swift\n.package(url: \"https://github.com/TizianoCoroneo/Deeplink\", .upToNextMinor(from: \"0.3.0\"))\n```\n\n## Using this library\n\nIn addition to the examples below, there is a DocC documentation that also contains a couple cool tutorials. \nYou can either [explore the documentation online](https://tizianocoroneo.github.io/Deeplink/documentation/deeplink/) or within Xcode: just select the option `Product \u003e Build Documentation` to get started (if you have Xcode 13 or later).\n\n### Defining deeplinks\n\nYou can start using this library by defining the deeplinks that you expect to receive in your application. You do this by creating templates:\n\n```swift\nlet mySellDeeplink: Deeplink\u003cVoid\u003e = \"/sell/ticket\"\nlet myEventDeeplink = \"/event/recommended\" as Deeplink\u003cVoid\u003e\n```\n\nNote that the `Deeplink` type has a generic type variable `Value`: if you need to extract data from the received URL, you can define the deeplink using string interpolation to specify which parts of the URL should be assigned to which property of an instance of `Value`.\nThis will attempt to pattern match the string components of the interpolation (throwing an error if the strings don't match), while assigning the extra parts of the URL to the property specified by keypaths from the argument components.\n\nExample:\n\n```swift\nstruct TestData {\n    var id: String?\n    var text: String?\n}\n\nlet myTestDeeplink: Deeplink\u003cTestData\u003e = try! \"/test/\\(\\.id)/\\(\\.text)\"\n```\n\nThe interpolation part requires a `WritableKeyPath\u003cValue, String?\u003e` where `Value` is your custom type. The property needs to be of type `String?`, and needs to be a `var` to let the library write the new value into it. \n\n### Using deeplinks to match/parse URLs\n\nThis deeplink template is able to match URLs that look like this:\n`https://ticketswap.com/test/123/abc`\n`https://ticketswap.com/test/123/abc?some=else`\n`https://ticketswap.com/test/123/abc#fragment`\n`ticketswap:///test/123/abc#fragment`\n\nAnd it is able to extract the path components `123` and `abc`, and assign them to the properties `id` and `text` on an instance of `TestData` like this:\n```swift\n\n// Define the deeplink\nlet myTestDeeplink: Deeplink\u003cTestData\u003e = try! \"/test/\\(\\.id)/\\(\\.text)\"\n\n// Example URL\nlet url = URL(string: \"https://ticketswap.com/test/123/abc\")!\n\n// Object to write data into\nvar result = TestData(id: \"\", text: \"\")\n\n// Use the deeplink to parse the URL, extracting its data into `result`\ntry myTestDeeplink.parse(url, into: \u0026result)\n\n// Check result's content\nprint(result.id) // Will print `123`\nprint(result.text) // Will print `abc`\n```\n\nIn the first example we used a `Deeplink\u003cVoid\u003e` to show that there is no argument in the deeplink template: these kinds of deeplink can be used if you only need to match a URL that has no variable data in it:\n```swift\n// Define the deeplink\nlet myTestDeeplink: Deeplink\u003cTestData\u003e = try! \"/test/ticket\"\n\n// Example URL\nlet url = URL(string: \"https://ticketswap.com/test/ticket\")!\n\n// Use the deeplink to parse the URL. Will throw if the URL relative part doesn't match the deeplink template. \ntry myTestDeeplink.parse(url)\n```\n\nIn case a property in `TestData` goes unused in a Deeplink template, that property will keep its initial value, which is `nil` unless you customized `TestData` init. Otherwise, if an argument is present in the template, but the URL contains no value for it, the property will assume a value of `\"\"`.\n\n### Pattern matching with multiple deeplinks\n\nThe typical use-case for a mobile app is to receive a URL through the `AppDelegate` with the method `application(_: open: options:)`, try to recognize which type of link it is (typically by using `URLComponents`), and matching piece by piece while extracting the needed data.\n\nThis library lets you take a more declarative approach: just define a list of deeplinks that you need to be able to match, and the corresponding action that you want to take when one is recognized. When a URL is received, every deeplink template will be tried one at the time until the correct one is found, the data is extracted and the corresponding action is triggered:\n\n```swift\n\n// This is the object that holds the list of deeplinks to try.\nlet center = DeeplinksCenter()\n\n// Data types where to store parsed values\nstruct Artist: Equatable {\n    var id: String?\n    var slug: String?\n}\n\nstruct Location: Equatable {\n    var id: String?\n    var slug: String?\n    var period: String?\n}\n\nstruct Event: Equatable {\n    var id: String?\n    var slug: String?\n}\n\n// Instances where to put the parsed data\nvar artist = Artist()\nvar location = Location()\nvar event = Event()\n\n// URLs to parse\nlet artistURL = URL(string: \"https://ticketswap.com/artist/metallica/123456\")!\nlet locationURL = URL(string: \"https://ticketswap.com/location/amsterdam/1234567/24-06-2019\")!\nlet eventURL = URL(string: \"https://ticketswap.com/event/awakenings/123\")!\n\n// Deeplink templates\nlet artistDeeplink = try! \"/artist/\\(\\.slug)/\\(\\.id)\"\n    as Deeplink\u003cArtist\u003e\n\nlet locationDeeplink = try! \"/location/\\(\\.slug)/\\(\\.id)/\\(\\.period)\"\n    as Deeplink\u003cLocation\u003e\n\nlet eventDeeplink = try! \"/event/\\(\\.slug)/\\(\\.id)\"\n    as Deeplink\u003cEvent\u003e\n\n// Registering a deeplink template into the center, using the `artistDeeplink` to parse data into the `artist` var, and run the `ifMatching` closure if the template matches a URL.\ncenter.register(\n    deeplink: artistDeeplink,\n    assigningTo: artist,\n    ifMatching: { url, newArtist in\n\n    // The parsed artist info is available here\n    if let id = newArtist.id {\n        print(id)\n    }\n    \n    // Indicates that we successfully handled the URL.\n    // Returning `false` makes the center evaluate the rest of the list of registered deeplinks.\n    return true\n})\n\n// Same registration for the location deeplink\n.register(\n    deeplink: locationDeeplink,\n    assigningTo: location,\n    ifMatching: { url, newLocation in\n\n    // The parsed location info is available here\n    if let id = newLocation.id {\n        print(id)\n    }\n    \n    return true\n})\n\n// Same registration for the event deeplink\n.register(\n    deeplink: eventDeeplink,\n    assigningTo: event,\n    ifMatching: { url, newEvent in\n\n    // The parsed event info is available here\n    if let id = newEvent.id {\n        print(id)\n    }\n    \n    return true\n})\n\n// This will match the artist deeplink, extract the data and print \"123456\".\ntry center.parse(url: artistURL)\n\n// This will match the location deeplink, extract the data and print \"1234567\".\ntry center.parse(url: locationURL)\n\n// This will match the event deeplink, extract the data and print \"123\".\ntry center.parse(url: eventURL)\n```\n\n### Result builder\n\nYou can take advantage of the shiny new result builders to register your deeplink templates: \n```swift\nfileprivate struct TestData: DefaultInitializable {\n    var arg1: String?\n    var arg2: String?\n}\n\nfileprivate struct TestData2 {\n    var arg1: String?\n    var arg2: String?\n}\n\nlet link1 = \"/test/1\" as Deeplink\u003cVoid\u003e\nlet link2 = try \"/test/\\(\\.arg1)/\\(\\.arg2)\" as Deeplink\u003cTestData\u003e\nlet link3 = try \"/test2/\\(\\.arg1)/\\(\\.arg2)\" as Deeplink\u003cTestData2\u003e\n\nlet center = DeeplinksCenter {\n\n    link1 { url in\n        XCTAssertEqual(url.absoluteString, \"https://apple.com/test/1\")\n        expectSimpleLink.fulfill()\n        return true\n    }\n\n    link2 { url, value in\n        XCTAssertEqual(url.absoluteString, \"https://apple.com/test/a/b\")\n        XCTAssertEqual(value.arg1, \"a\")\n        XCTAssertEqual(value.arg2, \"b\")\n        expectInitializableDataLink.fulfill()\n        return true\n    }\n\n    link3(\n        assigningTo: .init(arg1: \"default\", arg2: \"default\")\n    ) { (url, value) -\u003e Bool in\n        XCTAssertEqual(url.absoluteString, \"https://apple.com/test2/a/b\")\n        XCTAssertEqual(value.arg1, \"a\")\n        XCTAssertEqual(value.arg2, \"b\")\n        expectDataLink.fulfill()\n        return true\n    }\n}\n\ntry center.parse(url: URL(string: \"https://apple.com/test/1\")!)\ntry center.parse(url: URL(string: \"https://apple.com/test/a/b\")!)\ntry center.parse(url: URL(string: \"https://apple.com/test2/a/b\")!)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTizianoCoroneo%2FDeeplink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTizianoCoroneo%2FDeeplink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTizianoCoroneo%2FDeeplink/lists"}