URLComponents.url is nil
It looks like path
parameter's string must start with /
.
So change "auth/login"
to "/auth/login"
will do.
TL;DR
If you're creating a web URL (e.g. http://example.com/path
), include the /
because it uses the following template:
{scheme}://{user}:{password}@{host}:{port}{path}?{query}#{fragment}
If you're creating a non web URL (e.g. mailto:user@example.com
), don't include the /
because it uses the following template:
{scheme}:{path}?{query}%23{fragment}
To clarify why this is happening, the actual template of URLComponents is as follows:
{scheme}://{user}:{password}@{host}:{port}{path}?{query}#{fragment}
Notice how there is punctuation (://
, :
, @
, :
, ?
, #
) between every two components, except for {port}
and {path}
.
Therefore, {path}
must be prefixed with the punctuation (/
), otherwise it'll produce an invalid URL.
In your case you must use :
urlComponents.path = "/auth/login"
Note that even if you omit URLComponents's uncommon fields, you still run into the same problem:
{scheme}://{host}{path}?{query}#{fragment}
Notice how {host}
and {path}
are adjacent in this case as well, with no punctuation separating the two.
You may be wondering why, then, Apple didn't include the punctuation before {port}
. My guess is because URLComponents can be used to produce any type of URL in the RFC 3986 spec, not only Web URLs. E.g. it could easily produce http://www.example.com/foo
, but it should just as easily produce a URL of the form mailto:user@example.com
.
URLComponents makes this possible by changing the delimiter between {scheme}
and {path}
when {host}
is omitted:
{scheme}:{path}?{query}%23{fragment}
Thus when we want to produce a mailto link, we would actually not include the /
in path:
urlComponents.scheme = "mailto"urlComponents.path = "user@example.com"// urlComponents.url!.absoluteString == "mailto:user@example.com"
Comparing the properties of URLComponents to URL's properties provides a bit more insight (and reveals a bit of inconsistency):
let string = "scheme://user:password@host:123/path/path/path/?query#fragment"let urlComponents = URLComponents(string: string)!let url = URL(string: string)!// these are expectedurl.absoluteString == urlComponents.url!.absoluteStringurl.scheme == urlComponents.schemeurl.user == urlComponents.userurl.password == urlComponents.passwordurl.host == urlComponents.hosturl.port == urlComponents.porturl.query == urlComponents.queryurl.fragment == urlComponents.fragment// this is unexpected. They should have been equal to each otherurl.path != urlComponents.pathurl.path == "/path/path/path"urlComponents.path == "/path/path/path/"url.pathComponents == ["/", "path", "path", "path"]url.lastPathComponent == "path"
Two things to note:
url.path
doesn't have/
in the suffix.url.pathComponents[0]
is the first/
in the URL.
For an exhaustive list of templates, you can use this code:
let componentsCount = Double(8)let iterations = Int(pow(2, componentsCount))for i in 0..<iterations { var urlComponents = URLComponents() var key = "" if i & 0b10000000 > 0 { urlComponents.scheme = "scheme"; key += "s" } else { key += " " } if i & 0b01000000 > 0 { urlComponents.user = "user"; key += "u" } else { key += " " } if i & 0b00100000 > 0 { urlComponents.password = "password"; key += "w" } else { key += " " } if i & 0b00010000 > 0 { urlComponents.host = "host"; key += "h" } else { key += " " } if i & 0b00001000 > 0 { urlComponents.port = 123; key += "r" } else { key += " " } if i & 0b00000100 > 0 { urlComponents.path = "/path"; key += "p" } else { key += " " } if i & 0b00000010 > 0 { urlComponents.query = "query"; key += "q" } else { key += " " } if i & 0b00000001 > 0 { urlComponents.fragment = "fragment"; key += "f" } else { key += " " } if let url = urlComponents.url?.absoluteString { print("[\(key)] \(url)") } else { print("[\(key)] ------- nil --------") }}
Which produces the following output:
[ ] [ f] #fragment[ q ] ?query[ qf] ?query#fragment[ p ] /path[ p f] /path#fragment[ pq ] /path?query[ pqf] /path?query#fragment[ r ] //:123[ r f] //:123#fragment[ r q ] //:123?query[ r qf] //:123?query#fragment[ rp ] //:123/path[ rp f] //:123/path#fragment[ rpq ] //:123/path?query[ rpqf] //:123/path?query#fragment[ h ] //host[ h f] //host#fragment[ h q ] //host?query[ h qf] //host?query#fragment[ h p ] //host/path[ h p f] //host/path#fragment[ h pq ] //host/path?query[ h pqf] //host/path?query#fragment[ hr ] //host:123[ hr f] //host:123#fragment[ hr q ] //host:123?query[ hr qf] //host:123?query#fragment[ hrp ] //host:123/path[ hrp f] //host:123/path#fragment[ hrpq ] //host:123/path?query[ hrpqf] //host:123/path?query#fragment[ w ] //:password@[ w f] //:password@#fragment[ w q ] //:password@?query[ w qf] //:password@?query#fragment[ w p ] //:password@/path[ w p f] //:password@/path#fragment[ w pq ] //:password@/path?query[ w pqf] //:password@/path?query#fragment[ w r ] //:password@:123[ w r f] //:password@:123#fragment[ w r q ] //:password@:123?query[ w r qf] //:password@:123?query#fragment[ w rp ] //:password@:123/path[ w rp f] //:password@:123/path#fragment[ w rpq ] //:password@:123/path?query[ w rpqf] //:password@:123/path?query#fragment[ wh ] //:password@host[ wh f] //:password@host#fragment[ wh q ] //:password@host?query[ wh qf] //:password@host?query#fragment[ wh p ] //:password@host/path[ wh p f] //:password@host/path#fragment[ wh pq ] //:password@host/path?query[ wh pqf] //:password@host/path?query#fragment[ whr ] //:password@host:123[ whr f] //:password@host:123#fragment[ whr q ] //:password@host:123?query[ whr qf] //:password@host:123?query#fragment[ whrp ] //:password@host:123/path[ whrp f] //:password@host:123/path#fragment[ whrpq ] //:password@host:123/path?query[ whrpqf] //:password@host:123/path?query#fragment[ u ] //user@[ u f] //user@#fragment[ u q ] //user@?query[ u qf] //user@?query#fragment[ u p ] //user@/path[ u p f] //user@/path#fragment[ u pq ] //user@/path?query[ u pqf] //user@/path?query#fragment[ u r ] //user@:123[ u r f] //user@:123#fragment[ u r q ] //user@:123?query[ u r qf] //user@:123?query#fragment[ u rp ] //user@:123/path[ u rp f] //user@:123/path#fragment[ u rpq ] //user@:123/path?query[ u rpqf] //user@:123/path?query#fragment[ u h ] //user@host[ u h f] //user@host#fragment[ u h q ] //user@host?query[ u h qf] //user@host?query#fragment[ u h p ] //user@host/path[ u h p f] //user@host/path#fragment[ u h pq ] //user@host/path?query[ u h pqf] //user@host/path?query#fragment[ u hr ] //user@host:123[ u hr f] //user@host:123#fragment[ u hr q ] //user@host:123?query[ u hr qf] //user@host:123?query#fragment[ u hrp ] //user@host:123/path[ u hrp f] //user@host:123/path#fragment[ u hrpq ] //user@host:123/path?query[ u hrpqf] //user@host:123/path?query#fragment[ uw ] //user:password@[ uw f] //user:password@#fragment[ uw q ] //user:password@?query[ uw qf] //user:password@?query#fragment[ uw p ] //user:password@/path[ uw p f] //user:password@/path#fragment[ uw pq ] //user:password@/path?query[ uw pqf] //user:password@/path?query#fragment[ uw r ] //user:password@:123[ uw r f] //user:password@:123#fragment[ uw r q ] //user:password@:123?query[ uw r qf] //user:password@:123?query#fragment[ uw rp ] //user:password@:123/path[ uw rp f] //user:password@:123/path#fragment[ uw rpq ] //user:password@:123/path?query[ uw rpqf] //user:password@:123/path?query#fragment[ uwh ] //user:password@host[ uwh f] //user:password@host#fragment[ uwh q ] //user:password@host?query[ uwh qf] //user:password@host?query#fragment[ uwh p ] //user:password@host/path[ uwh p f] //user:password@host/path#fragment[ uwh pq ] //user:password@host/path?query[ uwh pqf] //user:password@host/path?query#fragment[ uwhr ] //user:password@host:123[ uwhr f] //user:password@host:123#fragment[ uwhr q ] //user:password@host:123?query[ uwhr qf] //user:password@host:123?query#fragment[ uwhrp ] //user:password@host:123/path[ uwhrp f] //user:password@host:123/path#fragment[ uwhrpq ] //user:password@host:123/path?query[ uwhrpqf] //user:password@host:123/path?query#fragment[s ] scheme:[s f] scheme:%23fragment[s q ] scheme:?query[s qf] scheme:?query%23fragment[s p ] scheme:/path[s p f] scheme:/path#fragment[s pq ] scheme:/path?query[s pqf] scheme:/path?query#fragment[s r ] scheme://:123[s r f] scheme://:123#fragment[s r q ] scheme://:123?query[s r qf] scheme://:123?query#fragment[s rp ] scheme://:123/path[s rp f] scheme://:123/path#fragment[s rpq ] scheme://:123/path?query[s rpqf] scheme://:123/path?query#fragment[s h ] scheme://host[s h f] scheme://host#fragment[s h q ] scheme://host?query[s h qf] scheme://host?query#fragment[s h p ] scheme://host/path[s h p f] scheme://host/path#fragment[s h pq ] scheme://host/path?query[s h pqf] scheme://host/path?query#fragment[s hr ] scheme://host:123[s hr f] scheme://host:123#fragment[s hr q ] scheme://host:123?query[s hr qf] scheme://host:123?query#fragment[s hrp ] scheme://host:123/path[s hrp f] scheme://host:123/path#fragment[s hrpq ] scheme://host:123/path?query[s hrpqf] scheme://host:123/path?query#fragment[s w ] scheme://:password@[s w f] scheme://:password@#fragment[s w q ] scheme://:password@?query[s w qf] scheme://:password@?query#fragment[s w p ] scheme://:password@/path[s w p f] scheme://:password@/path#fragment[s w pq ] scheme://:password@/path?query[s w pqf] scheme://:password@/path?query#fragment[s w r ] scheme://:password@:123[s w r f] scheme://:password@:123#fragment[s w r q ] scheme://:password@:123?query[s w r qf] scheme://:password@:123?query#fragment[s w rp ] scheme://:password@:123/path[s w rp f] scheme://:password@:123/path#fragment[s w rpq ] scheme://:password@:123/path?query[s w rpqf] scheme://:password@:123/path?query#fragment[s wh ] scheme://:password@host[s wh f] scheme://:password@host#fragment[s wh q ] scheme://:password@host?query[s wh qf] scheme://:password@host?query#fragment[s wh p ] scheme://:password@host/path[s wh p f] scheme://:password@host/path#fragment[s wh pq ] scheme://:password@host/path?query[s wh pqf] scheme://:password@host/path?query#fragment[s whr ] scheme://:password@host:123[s whr f] scheme://:password@host:123#fragment[s whr q ] scheme://:password@host:123?query[s whr qf] scheme://:password@host:123?query#fragment[s whrp ] scheme://:password@host:123/path[s whrp f] scheme://:password@host:123/path#fragment[s whrpq ] scheme://:password@host:123/path?query[s whrpqf] scheme://:password@host:123/path?query#fragment[su ] scheme://user@[su f] scheme://user@#fragment[su q ] scheme://user@?query[su qf] scheme://user@?query#fragment[su p ] scheme://user@/path[su p f] scheme://user@/path#fragment[su pq ] scheme://user@/path?query[su pqf] scheme://user@/path?query#fragment[su r ] scheme://user@:123[su r f] scheme://user@:123#fragment[su r q ] scheme://user@:123?query[su r qf] scheme://user@:123?query#fragment[su rp ] scheme://user@:123/path[su rp f] scheme://user@:123/path#fragment[su rpq ] scheme://user@:123/path?query[su rpqf] scheme://user@:123/path?query#fragment[su h ] scheme://user@host[su h f] scheme://user@host#fragment[su h q ] scheme://user@host?query[su h qf] scheme://user@host?query#fragment[su h p ] scheme://user@host/path[su h p f] scheme://user@host/path#fragment[su h pq ] scheme://user@host/path?query[su h pqf] scheme://user@host/path?query#fragment[su hr ] scheme://user@host:123[su hr f] scheme://user@host:123#fragment[su hr q ] scheme://user@host:123?query[su hr qf] scheme://user@host:123?query#fragment[su hrp ] scheme://user@host:123/path[su hrp f] scheme://user@host:123/path#fragment[su hrpq ] scheme://user@host:123/path?query[su hrpqf] scheme://user@host:123/path?query#fragment[suw ] scheme://user:password@[suw f] scheme://user:password@#fragment[suw q ] scheme://user:password@?query[suw qf] scheme://user:password@?query#fragment[suw p ] scheme://user:password@/path[suw p f] scheme://user:password@/path#fragment[suw pq ] scheme://user:password@/path?query[suw pqf] scheme://user:password@/path?query#fragment[suw r ] scheme://user:password@:123[suw r f] scheme://user:password@:123#fragment[suw r q ] scheme://user:password@:123?query[suw r qf] scheme://user:password@:123?query#fragment[suw rp ] scheme://user:password@:123/path[suw rp f] scheme://user:password@:123/path#fragment[suw rpq ] scheme://user:password@:123/path?query[suw rpqf] scheme://user:password@:123/path?query#fragment[suwh ] scheme://user:password@host[suwh f] scheme://user:password@host#fragment[suwh q ] scheme://user:password@host?query[suwh qf] scheme://user:password@host?query#fragment[suwh p ] scheme://user:password@host/path[suwh p f] scheme://user:password@host/path#fragment[suwh pq ] scheme://user:password@host/path?query[suwh pqf] scheme://user:password@host/path?query#fragment[suwhr ] scheme://user:password@host:123[suwhr f] scheme://user:password@host:123#fragment[suwhr q ] scheme://user:password@host:123?query[suwhr qf] scheme://user:password@host:123?query#fragment[suwhrp ] scheme://user:password@host:123/path[suwhrp f] scheme://user:password@host:123/path#fragment[suwhrpq ] scheme://user:password@host:123/path?query[suwhrpqf] scheme://user:password@host:123/path?query#fragment
Notice how none of these are nil
. If you change urlComponents.path = "/path"
to urlComponents.path = "path"
, however, you get a completely different set of templates:
[ ] [ f] #fragment[ q ] ?query[ qf] ?query#fragment[ p ] path[ p f] path#fragment[ pq ] path?query[ pqf] path?query#fragment[ r ] //:123[ r f] //:123#fragment[ r q ] //:123?query[ r qf] //:123?query#fragment[ rp ] ------- nil --------[ rp f] ------- nil --------[ rpq ] ------- nil --------[ rpqf] ------- nil --------[ h ] //host[ h f] //host#fragment[ h q ] //host?query[ h qf] //host?query#fragment[ h p ] ------- nil --------[ h p f] ------- nil --------[ h pq ] ------- nil --------[ h pqf] ------- nil --------[ hr ] //host:123[ hr f] //host:123#fragment[ hr q ] //host:123?query[ hr qf] //host:123?query#fragment[ hrp ] ------- nil --------[ hrp f] ------- nil --------[ hrpq ] ------- nil --------[ hrpqf] ------- nil --------[ w ] //:password@[ w f] //:password@#fragment[ w q ] //:password@?query[ w qf] //:password@?query#fragment[ w p ] ------- nil --------[ w p f] ------- nil --------[ w pq ] ------- nil --------[ w pqf] ------- nil --------[ w r ] //:password@:123[ w r f] //:password@:123#fragment[ w r q ] //:password@:123?query[ w r qf] //:password@:123?query#fragment[ w rp ] ------- nil --------[ w rp f] ------- nil --------[ w rpq ] ------- nil --------[ w rpqf] ------- nil --------[ wh ] //:password@host[ wh f] //:password@host#fragment[ wh q ] //:password@host?query[ wh qf] //:password@host?query#fragment[ wh p ] ------- nil --------[ wh p f] ------- nil --------[ wh pq ] ------- nil --------[ wh pqf] ------- nil --------[ whr ] //:password@host:123[ whr f] //:password@host:123#fragment[ whr q ] //:password@host:123?query[ whr qf] //:password@host:123?query#fragment[ whrp ] ------- nil --------[ whrp f] ------- nil --------[ whrpq ] ------- nil --------[ whrpqf] ------- nil --------[ u ] //user@[ u f] //user@#fragment[ u q ] //user@?query[ u qf] //user@?query#fragment[ u p ] ------- nil --------[ u p f] ------- nil --------[ u pq ] ------- nil --------[ u pqf] ------- nil --------[ u r ] //user@:123[ u r f] //user@:123#fragment[ u r q ] //user@:123?query[ u r qf] //user@:123?query#fragment[ u rp ] ------- nil --------[ u rp f] ------- nil --------[ u rpq ] ------- nil --------[ u rpqf] ------- nil --------[ u h ] //user@host[ u h f] //user@host#fragment[ u h q ] //user@host?query[ u h qf] //user@host?query#fragment[ u h p ] ------- nil --------[ u h p f] ------- nil --------[ u h pq ] ------- nil --------[ u h pqf] ------- nil --------[ u hr ] //user@host:123[ u hr f] //user@host:123#fragment[ u hr q ] //user@host:123?query[ u hr qf] //user@host:123?query#fragment[ u hrp ] ------- nil --------[ u hrp f] ------- nil --------[ u hrpq ] ------- nil --------[ u hrpqf] ------- nil --------[ uw ] //user:password@[ uw f] //user:password@#fragment[ uw q ] //user:password@?query[ uw qf] //user:password@?query#fragment[ uw p ] ------- nil --------[ uw p f] ------- nil --------[ uw pq ] ------- nil --------[ uw pqf] ------- nil --------[ uw r ] //user:password@:123[ uw r f] //user:password@:123#fragment[ uw r q ] //user:password@:123?query[ uw r qf] //user:password@:123?query#fragment[ uw rp ] ------- nil --------[ uw rp f] ------- nil --------[ uw rpq ] ------- nil --------[ uw rpqf] ------- nil --------[ uwh ] //user:password@host[ uwh f] //user:password@host#fragment[ uwh q ] //user:password@host?query[ uwh qf] //user:password@host?query#fragment[ uwh p ] ------- nil --------[ uwh p f] ------- nil --------[ uwh pq ] ------- nil --------[ uwh pqf] ------- nil --------[ uwhr ] //user:password@host:123[ uwhr f] //user:password@host:123#fragment[ uwhr q ] //user:password@host:123?query[ uwhr qf] //user:password@host:123?query#fragment[ uwhrp ] ------- nil --------[ uwhrp f] ------- nil --------[ uwhrpq ] ------- nil --------[ uwhrpqf] ------- nil --------[s ] scheme:[s f] scheme:%23fragment[s q ] scheme:?query[s qf] scheme:?query%23fragment[s p ] scheme:path[s p f] scheme:path%23fragment[s pq ] scheme:path?query[s pqf] scheme:path?query%23fragment[s r ] scheme://:123[s r f] scheme://:123#fragment[s r q ] scheme://:123?query[s r qf] scheme://:123?query#fragment[s rp ] ------- nil --------[s rp f] ------- nil --------[s rpq ] ------- nil --------[s rpqf] ------- nil --------[s h ] scheme://host[s h f] scheme://host#fragment[s h q ] scheme://host?query[s h qf] scheme://host?query#fragment[s h p ] ------- nil --------[s h p f] ------- nil --------[s h pq ] ------- nil --------[s h pqf] ------- nil --------[s hr ] scheme://host:123[s hr f] scheme://host:123#fragment[s hr q ] scheme://host:123?query[s hr qf] scheme://host:123?query#fragment[s hrp ] ------- nil --------[s hrp f] ------- nil --------[s hrpq ] ------- nil --------[s hrpqf] ------- nil --------[s w ] scheme://:password@[s w f] scheme://:password@#fragment[s w q ] scheme://:password@?query[s w qf] scheme://:password@?query#fragment[s w p ] ------- nil --------[s w p f] ------- nil --------[s w pq ] ------- nil --------[s w pqf] ------- nil --------[s w r ] scheme://:password@:123[s w r f] scheme://:password@:123#fragment[s w r q ] scheme://:password@:123?query[s w r qf] scheme://:password@:123?query#fragment[s w rp ] ------- nil --------[s w rp f] ------- nil --------[s w rpq ] ------- nil --------[s w rpqf] ------- nil --------[s wh ] scheme://:password@host[s wh f] scheme://:password@host#fragment[s wh q ] scheme://:password@host?query[s wh qf] scheme://:password@host?query#fragment[s wh p ] ------- nil --------[s wh p f] ------- nil --------[s wh pq ] ------- nil --------[s wh pqf] ------- nil --------[s whr ] scheme://:password@host:123[s whr f] scheme://:password@host:123#fragment[s whr q ] scheme://:password@host:123?query[s whr qf] scheme://:password@host:123?query#fragment[s whrp ] ------- nil --------[s whrp f] ------- nil --------[s whrpq ] ------- nil --------[s whrpqf] ------- nil --------[su ] scheme://user@[su f] scheme://user@#fragment[su q ] scheme://user@?query[su qf] scheme://user@?query#fragment[su p ] ------- nil --------[su p f] ------- nil --------[su pq ] ------- nil --------[su pqf] ------- nil --------[su r ] scheme://user@:123[su r f] scheme://user@:123#fragment[su r q ] scheme://user@:123?query[su r qf] scheme://user@:123?query#fragment[su rp ] ------- nil --------[su rp f] ------- nil --------[su rpq ] ------- nil --------[su rpqf] ------- nil --------[su h ] scheme://user@host[su h f] scheme://user@host#fragment[su h q ] scheme://user@host?query[su h qf] scheme://user@host?query#fragment[su h p ] ------- nil --------[su h p f] ------- nil --------[su h pq ] ------- nil --------[su h pqf] ------- nil --------[su hr ] scheme://user@host:123[su hr f] scheme://user@host:123#fragment[su hr q ] scheme://user@host:123?query[su hr qf] scheme://user@host:123?query#fragment[su hrp ] ------- nil --------[su hrp f] ------- nil --------[su hrpq ] ------- nil --------[su hrpqf] ------- nil --------[suw ] scheme://user:password@[suw f] scheme://user:password@#fragment[suw q ] scheme://user:password@?query[suw qf] scheme://user:password@?query#fragment[suw p ] ------- nil --------[suw p f] ------- nil --------[suw pq ] ------- nil --------[suw pqf] ------- nil --------[suw r ] scheme://user:password@:123[suw r f] scheme://user:password@:123#fragment[suw r q ] scheme://user:password@:123?query[suw r qf] scheme://user:password@:123?query#fragment[suw rp ] ------- nil --------[suw rp f] ------- nil --------[suw rpq ] ------- nil --------[suw rpqf] ------- nil --------[suwh ] scheme://user:password@host[suwh f] scheme://user:password@host#fragment[suwh q ] scheme://user:password@host?query[suwh qf] scheme://user:password@host?query#fragment[suwh p ] ------- nil --------[suwh p f] ------- nil --------[suwh pq ] ------- nil --------[suwh pqf] ------- nil --------[suwhr ] scheme://user:password@host:123[suwhr f] scheme://user:password@host:123#fragment[suwhr q ] scheme://user:password@host:123?query[suwhr qf] scheme://user:password@host:123?query#fragment[suwhrp ] ------- nil --------[suwhrp f] ------- nil --------[suwhrpq ] ------- nil --------[suwhrpqf] ------- nil --------