{"id":19220040,"url":"https://github.com/nativescript-community/universal-links","last_synced_at":"2025-04-09T20:11:15.228Z","repository":{"id":40382341,"uuid":"335320542","full_name":"nativescript-community/universal-links","owner":"nativescript-community","description":"Universal links (IOS) and App Links (Android) support for NativeScript.","archived":false,"fork":false,"pushed_at":"2024-12-02T16:14:07.000Z","size":3973,"stargazers_count":22,"open_issues_count":6,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-09T20:11:06.894Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/nativescript-community.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["farfromrefug"]}},"created_at":"2021-02-02T14:42:36.000Z","updated_at":"2024-12-02T16:14:10.000Z","dependencies_parsed_at":"2024-02-01T18:20:34.643Z","dependency_job_id":"12859f34-de86-405a-992e-580db0f48e3b","html_url":"https://github.com/nativescript-community/universal-links","commit_stats":{"total_commits":84,"total_committers":10,"mean_commits":8.4,"dds":0.5,"last_synced_commit":"e9a33b9f3df5861c0f7df0ac7364b50cf17b194f"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nativescript-community%2Funiversal-links","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nativescript-community%2Funiversal-links/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nativescript-community%2Funiversal-links/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nativescript-community%2Funiversal-links/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nativescript-community","download_url":"https://codeload.github.com/nativescript-community/universal-links/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"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":[],"created_at":"2024-11-09T14:33:46.117Z","updated_at":"2025-04-09T20:11:15.203Z","avatar_url":"https://github.com/nativescript-community.png","language":"TypeScript","funding_links":["https://github.com/sponsors/farfromrefug"],"categories":[],"sub_categories":[],"readme":"\u003c!-- ⚠️ This README has been generated from the file(s) \"blueprint.md\" ⚠️--\u003e\u003c!-- ⚠️ This README has been generated from the file(s) \"blueprint.md\" ⚠️--\u003e\n\u003c!--  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      DO NOT EDIT THIS READEME DIRECTLY! Edit \"bluesprint.md\" instead.\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --\u003e\n\u003ch1 align=\"center\"\u003e@nativescript-community/universal-links\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n\t\t\u003ca href=\"https://npmcharts.com/compare/@nativescript-community/universal-links?minimal=true\"\u003e\u003cimg alt=\"Downloads per month\" src=\"https://img.shields.io/npm/dm/@nativescript-community/universal-links.svg\" height=\"20\"/\u003e\u003c/a\u003e\r\n\u003ca href=\"https://www.npmjs.com/package/@nativescript-community/universal-links\"\u003e\u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/@nativescript-community/universal-links.svg\" height=\"20\"/\u003e\u003c/a\u003e\n\t\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eUniversal links (IOS) and App Links (Android) support for NativeScript.\u003c/b\u003e\u003c/br\u003e\n  \u003csub\u003e\u003csub\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n\nWhen a user clicks a link to a website, it opens in the default web browser (Safari/Chrome). Universal linking allows your app to open instead of the web browser.\n\nApple calls this _Universal Links_ and Google calls it _App Links_, but they mean the same thing.\n\n| \u003cimg src=\"https://raw.githubusercontent.com/nativescript-community/universal-links/master/images/demo-ios.gif\" height=\"500\" /\u003e | \u003cimg src=\"https://raw.githubusercontent.com/nativescript-community/universal-links/master/images/demo-android.gif\" height=\"500\" /\u003e |\n| --- | ----------- |\n| iOS Demo | Android Demo |\n\n\r\n[](#migration-to-3x)\r\n\r\n\r\n[](#migration-to-3x)\r\n\r\n## Migration to 3.x\n\nIn version 3.0.0 the returned object is now simply the link as a `string`. It is not \"parsed\" anymore. The reason for that is that `url-parse` package used for this is pretty huge and not needed by most.\nYou can still parse like so:\n```typescript\nimport * as urlparse from 'url-parse';\n\nfunction parseLink(link: string) {\n  const url = urlparse(link, true);\n  return {\n    href: url.href,\n    origin: url.origin\n    pathname: url.pathname,\n    query: url.query\n  }\n  \n}\n```\n\n\r\n[](#table-of-contents)\r\n\r\n\r\n[](#table-of-contents)\r\n\r\n## Table of Contents\n\n* [Migration to 3.x](#migration-to-3x)\r\n* [Installation](#installation)\r\n* [Implementing Universal Links](#implementing-universal-links)\r\n\t* [iOS](#ios)\r\n\t* [Android](#android)\r\n* [Usage](#usage)\r\n* [Debugging](#debugging)\r\n\t* [iOS](#ios-1)\r\n\t* [Android](#android-1)\r\n* [Demos and Development](#demos-and-development)\r\n\t* [Repo Setup](#repo-setup)\r\n\t* [Build](#build)\r\n\t* [Demos](#demos)\r\n* [Contributing](#contributing)\r\n\t* [Update repo ](#update-repo-)\r\n\t* [Update readme ](#update-readme-)\r\n\t* [Update doc ](#update-doc-)\r\n\t* [Publish](#publish)\r\n\t* [modifying submodules](#modifying-submodules)\r\n* [Questions](#questions)\n\n\r\n[](#installation)\r\n\r\n\r\n[](#installation)\r\n\r\n## Installation\nRun the following command from the root of your project:\n\n`ns plugin add @nativescript-community/universal-links`\n\n\r\n[](#implementing-universal-links)\r\n\r\n\r\n[](#implementing-universal-links)\r\n\r\n## Implementing Universal Links\n\nBoth iOS (9.0 and newer) and Android (all versions) provide good APIs for universal linking.\n\n### iOS\n\nApple introduced a new deep linking API in iOS 9.0 called “Universal Links”. It provides a better user experience than the hacky deep linking options that existed in iOS 8.0 and below.\n\nFirst step is to add a file to the root of your website called `apple-app-site-association`. This is a JSON file and it looks like this:\n\n```javascript\n{\n    \"applinks\": {\n        \"apps\": [],\n        \"details\": [\n            {\n                \"appID\": \"TEAM_ID.BUNDLE_ID\", // ex: \"9JA89QQLNQ.com.apple.wwdc\"\n                \"paths\": [ \"/blog/*\"]\n            }\n        ]\n    }\n}\n```\n\n- This file will be downloaded automatically by every single user that installs or upgrades your iOS app.\n- It **_MUST_** be served over HTTPS with a valid SSL certificate. If you need to test this, I recommend using https://ngrok.io.\n- This file is only fetched once when the user first installs or upgrades the app. It must live on your website before your app is released. This also means that you can’t add new deep linking url patterns to your app until you push out a new app update to force users to refresh the file.\n- I suggest using this [Apple App Site Association (AASA) Validator](https://branch.io/resources/aasa-validator/) to confirm your `apple-app-site-association` is correct.\n\nCheck out [Apples' docs](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW2) for more info.\n\nNext, you need to add the Associated Domains to your IOS project, either using XCode or manually adding the following code to your `App_Resources/IOS/app.entitlements` file. Please note the `applinks:` prefix, it won't work without it.\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n  \u003cdict\u003e\n    \u003ckey\u003ecom.apple.developer.associated-domains\u003c/key\u003e\n    \u003carray\u003e\n      \u003cstring\u003eapplinks:www.example.com\u003c/string\u003e\n    \u003c/array\u003e\n  \u003c/dict\u003e\n\u003c/plist\u003e\n```\n\n### Android\n\nIn Android, universal linking is implemented using Intent Filters. By adding a BROWSABLE intent filter, you are saying that your app can be started by a user clicking on a website url.\n\nYou don't need any server side changes for Android, only modify your app to add the Intent Filter.\nAdd this code to your `App_Resources/Android/src/main/AndroidManifest.xml` file:\n\n```xml\n\u003cactivity\n    android:name=\"com.tns.NativeScriptActivity\"\n    android:label=\"@string/title_activity_kimera\" \u003e\n\n    \u003c!-- Add this new section to your Activity --\u003e\n    \u003cintent-filter\u003e\n        \u003caction android:name=\"android.intent.action.VIEW\" /\u003e\n        \u003ccategory android:name=\"android.intent.category.DEFAULT\" /\u003e\n        \u003ccategory android:name=\"android.intent.category.BROWSABLE\" /\u003e\n\n        \u003c!-- Handle urls starting with \"https://www.example.com/blog\" --\u003e\n        \u003cdata android:scheme=\"https\"\n              android:host=\"www.example.com\"\n              android:pathPrefix=\"/blog\" /\u003e\n    \u003c/intent-filter\u003e\n\u003c/activity\u003e\n```\n\n\r\n[](#usage)\r\n\r\n\r\n[](#usage)\r\n\r\n## Usage\n\nCall the `registerUniversalLinkCallback` somewhere in the startup of your app. This Angular example puts it in the AppComponent's ngOnInit method to provide a callback method which will receive an Universal Link object every time your app is opened by a website link:\n\n```js\nimport { Component, OnInit } from \"@angular/core\";\nimport { registerUniversalLinkCallback } from \"@nativescript-community/universal-links\";\n\n@Component({\n  selector: \"my-app\",\n  template: \"\u003cpage-router-outlet\u003e\u003c/page-router-outlet\u003e\"\n})\nexport class AppComponent {\n  constructor() {}\n\n  ngOnInit() {\n    registerUniversalLinkCallback(ul =\u003e {\n      // use the router to navigate to the screen\n    });\n  }\n}\n```\n\nThe universal link object has the following structure:\n\n```JSON\n{\n  \"href\": \"https://www.example.com/blog?title=welcome\",\n  \"origin\": \"https://www.example.com\",\n  \"pathname\": \"/blog\",\n  \"query\": \"?title=welcome\"\n}\n```\n\nThere is also a `getUniversalLink()` method that will return the last universal link which opened the app. This is useful in scenarios where your app is protected by a login screen. Check if the user is authenticated and then navigate to the desired path.\n\n```js\nimport { getUniversalLink } from \"nativescript-plugin-universal-links\";\n\nconst ul = getUniversalLink();\n```\n\r\n[](#debugging)\r\n\r\n\r\n[](#debugging)\r\n\r\n## Debugging\n\nYou can simulate universal links to debug within your app\n\n### iOS\n\n```shell\nxcrun simctl openurl booted \"*your link*\"\n```\n\n### Android\n\n```shell\nadb shell am start -d \"*your link*\"\n``````\n\n\r\n[](#demos-and-development)\r\n\r\n\r\n[](#demos-and-development)\r\n\r\n## Demos and Development\n\n\n### Repo Setup\n\nThe repo uses submodules. If you did not clone with ` --recursive` then you need to call\n```\ngit submodule update --init\n```\n\nThe package manager used to install and link dependencies must be `pnpm` or `yarn`. `npm` wont work.\n\nTo develop and test:\nif you use `yarn` then run `yarn`\nif you use `pnpm` then run `pnpm i`\n\n**Interactive Menu:**\n\nTo start the interactive menu, run `npm start` (or `yarn start` or `pnpm start`). This will list all of the commonly used scripts.\n\n### Build\n\n```bash\nnpm run build.all\n```\nWARNING: it seems `yarn build.all` wont always work (not finding binaries in `node_modules/.bin`) which is why the doc explicitly uses `npm run`\n\n### Demos\n\n```bash\nnpm run demo.[ng|react|svelte|vue].[ios|android]\n\nnpm run demo.svelte.ios # Example\n```\n\nDemo setup is a bit special in the sense that if you want to modify/add demos you dont work directly in `demo-[ng|react|svelte|vue]`\nInstead you work in `demo-snippets/[ng|react|svelte|vue]`\nYou can start from the `install.ts` of each flavor to see how to register new demos \n\n\r\n[](#contributing)\r\n\r\n\r\n[](#contributing)\r\n\r\n## Contributing\n\n### Update repo \n\nYou can update the repo files quite easily\n\nFirst update the submodules\n\n```bash\nnpm run update\n```\n\nThen commit the changes\nThen update common files\n\n```bash\nnpm run sync\n```\nThen you can run `yarn|pnpm`, commit changed files if any\n\n### Update readme \n```bash\nnpm run readme\n```\n\n### Update doc \n```bash\nnpm run doc\n```\n\n### Publish\n\nThe publishing is completely handled by `lerna` (you can add `-- --bump major` to force a major release)\nSimply run \n```shell\nnpm run publish\n```\n\n### modifying submodules\n\nThe repo uses https:// for submodules which means you won't be able to push directly into the submodules.\nOne easy solution is t modify `~/.gitconfig` and add\n```\n[url \"ssh://git@github.com/\"]\n\tpushInsteadOf = https://github.com/\n```\n\n\r\n[](#questions)\r\n\r\n\r\n[](#questions)\r\n\r\n## Questions\n\nIf you have any questions/issues/comments please feel free to create an issue or start a conversation in the [NativeScript Community Discord](https://nativescript.org/discord).\n\r\n[](#demos-and-development)\r\n\r\n## Demos and Development\n\n\n### Repo Setup\n\nThe repo uses submodules. If you did not clone with ` --recursive` then you need to call\n```\ngit submodule update --init\n```\n\nThe package manager used to install and link dependencies must be `pnpm` or `yarn`. `npm` wont work.\n\nTo develop and test:\nif you use `yarn` then run `yarn`\nif you use `pnpm` then run `pnpm i`\n\n**Interactive Menu:**\n\nTo start the interactive menu, run `npm start` (or `yarn start` or `pnpm start`). This will list all of the commonly used scripts.\n\n### Build\n\n```bash\nnpm run build.all\n```\nWARNING: it seems `yarn build.all` wont always work (not finding binaries in `node_modules/.bin`) which is why the doc explicitly uses `npm run`\n\n### Demos\n\n```bash\nnpm run demo.[ng|react|svelte|vue].[ios|android]\n\nnpm run demo.svelte.ios # Example\n```\n\nDemo setup is a bit special in the sense that if you want to modify/add demos you dont work directly in `demo-[ng|react|svelte|vue]`\nInstead you work in `demo-snippets/[ng|react|svelte|vue]`\nYou can start from the `install.ts` of each flavor to see how to register new demos \n\n\r\n[](#contributing)\r\n\r\n## Contributing\n\n### Update repo \n\nYou can update the repo files quite easily\n\nFirst update the submodules\n\n```bash\nnpm run update\n```\n\nThen commit the changes\nThen update common files\n\n```bash\nnpm run sync\n```\nThen you can run `yarn|pnpm`, commit changed files if any\n\n### Update readme \n```bash\nnpm run readme\n```\n\n### Update doc \n```bash\nnpm run doc\n```\n\n### Publish\n\nThe publishing is completely handled by `lerna` (you can add `-- --bump major` to force a major release)\nSimply run \n```shell\nnpm run publish\n```\n\n### modifying submodules\n\nThe repo uses https:// for submodules which means you won't be able to push directly into the submodules.\nOne easy solution is t modify `~/.gitconfig` and add\n```\n[url \"ssh://git@github.com/\"]\n\tpushInsteadOf = https://github.com/\n```\n\r\n[](#questions)\r\n\r\n## Questions\n\nIf you have any questions/issues/comments please feel free to create an issue or start a conversation in the [NativeScript Community Discord](https://nativescript.org/discord).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnativescript-community%2Funiversal-links","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnativescript-community%2Funiversal-links","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnativescript-community%2Funiversal-links/lists"}