{"id":17328296,"url":"https://github.com/whtsky/openermanifest","last_synced_at":"2025-04-13T14:32:55.306Z","repository":{"id":142156189,"uuid":"39628313","full_name":"whtsky/OpenerManifest","owner":"whtsky","description":"Set of rules powering Opener for iOS","archived":false,"fork":false,"pushed_at":"2024-04-01T20:55:03.000Z","size":30,"stargazers_count":2,"open_issues_count":1,"forks_count":29,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T05:28:39.280Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":false,"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/whtsky.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":"2015-07-24T11:10:24.000Z","updated_at":"2025-02-07T01:07:19.000Z","dependencies_parsed_at":"2024-12-05T17:33:43.371Z","dependency_job_id":null,"html_url":"https://github.com/whtsky/OpenerManifest","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whtsky%2FOpenerManifest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whtsky%2FOpenerManifest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whtsky%2FOpenerManifest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whtsky%2FOpenerManifest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whtsky","download_url":"https://codeload.github.com/whtsky/OpenerManifest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248728268,"owners_count":21152190,"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-10-15T14:23:23.060Z","updated_at":"2025-04-13T14:32:55.278Z","avatar_url":"https://github.com/whtsky.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Opener Manifest\n\n[Opener](http://www.opener.link) is an app for iOS that allows people to open web links in native apps instead. It does so by transforming web links within an engine powered by a rule set. This repo is the public version of that rule set.\n\n## Apps\n\nThe `apps` top level key in the manifest contains an ordered list of dictionaries, each representing an app supported by Opener. Each app contains the following fields\n\n- `identifier` *string*: A human-readable identifier for this app, used elsewhere in the manifest.\n- `name` *string*: The user-facing name for this app within Opener.\n- `storeIdentifier` *number as string*: The identifier of the app on the App Store.\n- `scheme` *URL as string*: A URL containing only the scheme that will open this app.\n- `new` *bool*: Indicates whether or not this app will be include in the \"New Apps\" group in Opener. Evaluates to `false` if unspecified.\n- `platform` *string*: Specifies if this app should only show up on iPhone/iPod Touch (value=`phone`) or on iPad (value=`pad`), shows on both if unspecified. *(Opener 1.0.1 and above)*\n\nFor example, if Opener were to include itself as an app\n\n```\n{\n\t\"identifier\": \"opener\",\n\t\"storeIdentifier\": \"989565871\",\n\t\"name\": \"Opener\",\n\t\"scheme\": \"opener://\",\n\t\"new\": true\n}\n```\n\n\n## Actions\n\nThe `actions` top level key in the manifest contains a list of dictionaries, each corresponding to a web URL-to-native URL rule. There's a many-to-one relationship between the values in `actions` and `apps`.\n\n### Common values\n\n- `title` *string*: The user-facing title for this action.\n- `regex` *string*: A regular expression string that the input URL is matched against. If this regex is matched by Opener for a given input, this action will appear in the list of available opening options.\n- `includeHeaders` *bool*: Indicates if headers should be included in the string that `regex` is matched with. If `true`, the headers are included in the input as a JSON encoded string separated from the input URL by a newline. *(Opener 1.0.2 and above)*\n- `formats` *array of dictionaries*: Specifies the apps that an action can be opened in (see [below](#formats)).\n\n### \u003ca tag=\"formats\"\u003eFormats\u003c/a\u003e\n\nBecause an action could taken in multiple apps, there's an array within each action dictionary named `formats`. Each entry in this array matches the input URL with an app-specific output for the given action. Each of these contains the following keys.\n\n- `appIdentifier` *string*: The identifier of the app that this action applies to. Should match the `identifier` of an app.\n- `format` *string*: The regex template applied to the input. Mutually exclusive with `script`.\n\n### Advanced URL generation in formats\n\nSome app native URLs can't be generated using simple regex templating, they require lookups or encoding of some sort. To do this, action formats can provide Javascript methods that are executed to convert input URLs to app native action URLs.\n\n- `script` *Javascript string*: Mutually exclusive with `format`.\n\nThis script must contain a Javascript function named `process` that takes two inputs, a URL and an anonymous function to be called upon completion. Once complete, the completion handler should be called passing the result or `null` on failure.\n\nFor example\n\n```\nfunction process(url, completionHandler) {\n\t// do something with URL...\n\turl = rot13(url);\n\t\n\tcompletionHandler(url);\n}\n```\n\nOpener enforces a timeout of 15 seconds if `completionHandler` isn't called.\n\n#### Common scenarios\n\nIf you're planning on using the **Twitter Cards** tags embedded on a page, you should isolate the app's URL scheme and do something like this, which is used for [Swarm](https://www.swarmapp.com/).\n\n```\nfunction process(url, completionHandler) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 \u0026\u0026 xmlhttp.status == 200) { var res = xmlhttp.responseText; var regex = RegExp('.*(swarm:\\/\\/.*?)\\\".*'); var match = regex.exec(res)[1]; completionHandler(match); } }; xmlhttp.open('GET', url, true); xmlhttp.send(); }\n```\n\nIf you need to **URL encode** something, here's an example for [Overcast](https://overcast.fm/).\n\n```\nfunction process(url, completionHandler) { completionHandler('overcast://x-callback-url/add?url=' + encodeURIComponent(url)); }\n```\n\nIf you need to pick a component out of the URL passed in, you can use the Javascript `RegExp` class.\n\n### Testing\n\nTo keep Opener maintainable, tests for actions can and should be provided.\n\nAt the `action` level:\n\n- `testInputs` *array of strings*: An array of test inputs that will be run against `regex` then each action.\n\nAt the `format` level:\n\n- `testResults` *array of strings or null entries*:  An array of expected results for this format for each of the test inputs. `null` should be used to specify that a test input *should not* match.\n\nFor example\n\n```\n{\n\t...\n\t\"regex\": \"http(?:s)?://(?:www\\\\.)?foo\\.bar/(\\\\d+).*$\",\n\t\"testInputs\": [\n\t\t\"https://foo.bar/1234\"\n\t\t\"http://www.foo.bar/wat\"\n\t],\n\t\"formats\": [\n\t\t{\n\t\t\t...\n\t\t\t\"format\": \"foo-app://entry/$1\",\n\t\t\t\"testResults\": [\n\t\t\t\t\"foo-app://entry/1234\",\n\t\t\t\tnull\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t...\n\t\t\t\"script\": \"function process(url, completion) { completion('bar-app://' + encodeURIComponent(url)); }\",\n\t\t\t\"testResults\": [\n\t\t\t\t\"bar-app://https%3A%2F%2Ffoo.bar%2F1234\",\n\t\t\t\tnull\n\t\t\t]\n\t\t}\n\t]\n}\n```\n\n\nTesting formats that have `includeHeaders` is not currently possible.\n\n## Contributing\n\nPull requests are welcome! Because Opener is a closed source app with an experience that I'd like to keep great, I'm going to be pedantic about these requests. I will likely manipulate the order of the apps and actions that are added, and handle the `new` flag for them.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhtsky%2Fopenermanifest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhtsky%2Fopenermanifest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhtsky%2Fopenermanifest/lists"}