Handling custom protocol in URL with Elm (was: Forking elm/url to enable custom Url protocol)

I’m currently using Elm to develop a mobile application. In iOS you cannot serve pages via http/https locally because those are reserved for external sites, so you need to use a custom protocol (e. g. app://, my-cool-app://, etc.). This is not possible with elm/url due to this issue: Support protocols different from http / https · Issue #10 · elm/url · GitHub

So I dug through different strategies to solve this problem and the most compelling idea was this proposal by Evan:

However: while this theoretically works, I now have the problem that I cannot reexport </> and <?> because I’m not a blessed package author.

Is there a way to reexport it? Or has someone an idea on how to make the parser combination more readable?

Because

parser =
    Parser.map Params (slash (Parser.s "a") (slash (Parser.s "b") Parser.string))

is definitively less readable than

parser =
    Parser.map Params (Parser.s "a" </> Parser.s "b" </> Parser.string)

Edit: This solution also has the drawback that you cannot use elm/browser directly because it depends on elm/url

2 Likes

What I would try to do first is to capture all urls in order to rewritte them as strings. You can create a fake server name and do simple replace, like ‘https://ios’.

I’m not sure I fully understand what you’re proposing: do you mean catching all navigation attempts in JS or do you mean passing the urls over ports?

The latter technique is something I have used in the past (due to other circumstances) and it is very unpleasant to work with because:

  1. It requires deep knowledge of the system, since you cannot simply do page changes
  2. You have to effectively implement a push state router in JavaScript
  3. It is brittle and will go wrong - which is the reason to use Elm in the first place :slight_smile:

The former: I haven’t deeply thought about it, yet.

It could work in the case of clicked links (e. g. register an event listener on the document and preventDefault for each link that was clicked and then rewrite the URL) but it wouldn’t solve the problem of providing the “correct” URL to elm/browser.application.

For that I’d have to monkey patch the compiled Elm code… E. g. I’d have to overwrite _Browser_getUrl, _Browser_pushUrl and so on. I’m not sure that would be a good solution…


Edit: one could probably combine both approaches. Feeding the current url via Flag/Port in a format that is understood by Elm and a click handler that intercepts all links generated by Elm.

How about replacing Url.fromString with something like

fromString url =
    Url.fromString <|
        case String.split "://" url of
            [ a, b ] ->
                if a == "app" then
                    "https://" ++ b

                else
                    url

            _ ->
                url

@lucamug you mean monkey patching the resulting JS code?

Otherwise: sure this is a simple patch to get from one representation into the other. But it wouldn’t solve the problem that I cannot use elm/browser.application, or am I missing something?

I have found a solution that is at least acceptable.

For the iOS app I need a postprocessing step in the build tool. In this step I now simply replace “http://” with “file://” in Url.toString and Url.fromString. This runs on the optimized and minified elm.js file. That way I only lose the ability to link directly on http:// resources which is acceptable in my case.

As long as anyone on the team uses elm/url to create href attributes it should work as expected.

If anyone thinks about adopting this approach: this only works because the string "http://" is only present twice in the compiled app - in exactly those two functions. You also need to make sure to chose a replacement string that is exactly as long as the original, e. g. file://, abcd://, or whatever. Otherwise you would also have to adjust the length that is trimmed from the url.

2 Likes

I had similar problems, when I wanted to run my app locally from file:// or from other browsers with support for other protocols, such as hyper or ipfs … In my case the only solution was to patch the elm-url-lib …

Here is the link …

2 Likes

Thanks for the example. That’s an interesting approach!

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.