What is a URL in Foundation, and what does URL(string:) accept?
Predict first
What does this print on iOS 17 / macOS 14 or later?
print(URL(string: "https://example.com/a b") as Any)
- nil
- Optional(https://example.com/a%20b)
- Crash
Predict first
What does this print?
let u = URL(string: "https://user@example.com:8080/p/a/t/h?x=1#frag")!
print(u.host!, u.port!, u.lastPathComponent)
- example.com 8080 h
- user 8080 h
- example.com:8080 h
Mental model. A URL is a postal address written in a standard layout. It tells you where something is, not what is inside.
URL is a value type that holds a parsed address: scheme, host, port, path, query and fragment. It does not fetch anything; it is just a structured name. URL(string:) returns an optional because the text may not be a valid URL.
What counts as valid changed. Since iOS 17 and macOS 14, URL(string:) follows the newer standard and percent-encodes characters that used to be rejected, such as a space, so "https://x.com/a b" now succeeds. On older systems it returned nil. Pass encodingInvalidCharacters: false for strict parsing, and test on your oldest deployment target.
Newer Foundation percent-encodes the space instead of rejecting the string. Older versions returned nil.
host excludes the user and port, which have their own properties.
Common traps
- “
URL(string:)returns nil for bad input.” It depends on the OS: iOS 17 and later fix up spaces instead of failing. - Force-unwrapping a URL built from user input. Keep the optional and handle it.
They might follow up with
- Is
URLthread-safe to share? Yes. It is an immutable value. - What is the difference between
URLandURLRequest? A request adds method, headers, body and cache policy to the address.
Related questions
- 2.3.4 Why use URLComponents and URLQueryItem, and what do they still not encode for you?
- 2.3.2 What is the difference between a file URL and a web URL, and URL(fileURLWithPath:) and URL(string:)?
What is the difference between a file URL and a web URL, and… →