When I do
let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "//barfoo.com/bar/foo", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")
it prints ftp://barfoo.com/bar/foo
Is this expected behaviour? Is this documented somewhere?
[EDIT] I was just wondering, because I'd expect that all the stuff, including server and path would be taken from the base url, like here:
let baseUrl = URL(string: "ftp://www.foobar.com/foo/bar/")
let finalUrl = URL(string: "boo/far", relativeTo: baseUrl)
print(finalUrl?.absoluteString ?? "ooops")
which prints ftp://www.foobar.com/foo/bar/boo/far
.
Why is the server and everything else from the base url being ignored in the first example? Is that due to the //
at the beginning? And is this part of some RFC or something or documented somewhere else?
As you note, this is due to the //
at the beginning. From the URI RFC, the format of a URI is:
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
When you begin your URI with //
, you're indicating that it should replace the authority and path, which it does. As described in Section 3.3, a path-absolute
cannot begin with //
:
/ path-absolute ; begins with "/" but not "//"
If your intention is to replace the path without modifying the authority (userinfo, host, port), then you can pass an absolute path (beginning with /
) rather than an authority and path (beginning with //
):
let finalUrl = URL(string: "/barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/barfoo.com/bar/foo
And if you want to append a path, you can pass a rootless path (not beginning with a /
):
let finalUrl = URL(string: "barfoo.com/bar/foo", relativeTo: baseUrl)
// ftp://www.foobar.com/foo/barfoo.com/bar/foo
For operations more complex that this, you should definitely look at URLComponents instead, which allows you to query and modify parts more explicitly and safely.