URLComponents.url is nil URLComponents.url is nil swift swift

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 --------