What is going on here is that Foundation's URL type models two things - URLs and relative references, using a single type named "URL".
The problem is that a relative reference is not, by itself, an identifier; just as a set of directions cannot be used to identify a place - however, if we have a starting point (an origin, or "base URL"), we can resolve a set of directions relative to that origin, in order to arrive at a new identifier.
It is simply a conceptual error to combine them in a single type. These strings represent different data, and there are operations which may make sense for one but not the other.
What makes this even more challenging is that the URL API is confused and incorrect. URL purports to follow RFC-1738 and RFC-1808. The former gives us the most basic description of URL syntax:
In general, URLs are written as follows:
<scheme>:<scheme-specific-part>
A URL contains the name of the scheme being used (<scheme>) followed
by a colon and then a string (the <scheme-specific-part>) whose
interpretation depends on the scheme.
Basically - schemes are mandatory, everything else is optional. This is the basic premise of URLs.
RFC-1808 came along later, and defined "relative URLs". It describes itself as "a companion to RFC 1738, which specifies the syntax and semantics of absolute URLs", and gives us a basic description of what relative URLs look like:
The syntax for relative URLs is a shortened form of that for absolute
URLs, where some prefix of the URL is missing and certain path
components ("." and "..") have a special meaning when interpreting a
relative path.
This is a good way to think about these concepts - as companions, but not the same thing. URLs as defined by RFC-1738 are referred to as "absolute URLs" by this standard. Absolute URLs must begin with a scheme, and since relative URLs drop some prefix of their URL, they necessarily do not include a scheme. Fundamentally, that is how you tell absolute vs. relative URLs apart.
And yet, Foundation's URL type accepts relative URLs as input (without a scheme, and without specifying a base URL to get a scheme from), and includes a property called absoluteURL
which returns a non-optional URL. There is simply no way for that API to work:
URL(string: "foo")!.absoluteURL // "foo" - Not an absolute URL :(
In short, you're stumbling upon several weaknesses of Foundation's URL model. It combines separate concepts in a single type, which is a poor design, and its APIs demonstrate a confused understanding of URL concepts, which is of course not ideal for a URL library.
I've been creating a new URL library, WebURL, which remedies several of these flaws. A WebURL value always contains an absolute URL, and if you want to resolve a relative reference, you call the .resolve
function, which returns another absolute URL.
So things like WebURL("foo")
return nil, as you expect. It is also much more lenient about the correctly-structured URL strings it accepts, as it is built to match modern browsers. I've noticed in particular some social media libraries and applications using it, as Foundation sometimes fails to parse URLs which work in browsers and are hence used across the web.