Swift
URL Encoding
Programming
iOS Development
Coding Tutorial

How to encode a URL in Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When building iOS or macOS apps that interact with web APIs, you frequently need to encode URLs so that special characters are transmitted safely. Swift provides built-in tools for percent-encoding strings, but choosing the right character set and applying encoding at the right stage of your code matters. Incorrect encoding can break query parameters, corrupt path segments, or cause silent data loss.

This article walks through how URL encoding works, the Swift APIs available for it, and practical patterns for encoding different parts of a URL correctly.

Understanding URL Encoding

URL encoding, also called percent encoding, replaces characters that are not allowed in a URL with a % followed by two hexadecimal digits representing the character's byte value. For example, a space becomes %20, an ampersand becomes %26, and a plus sign becomes %2B.

The reason encoding is necessary is that certain characters have special meaning in URLs. The ? character separates the path from the query string, & separates query parameters, and # marks a fragment. If your data contains these characters, they must be encoded so the URL parser does not misinterpret them.

Encoding a Full URL String

The most common approach in Swift is the addingPercentEncoding(withAllowedCharacters:) method on String. You pass a CharacterSet that specifies which characters should remain unencoded.

swift
1let rawURL = "https://api.example.com/search?query=hello world&lang=en"
2
3if let encoded = rawURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
4    print(encoded)
5    // https://api.example.com/search?query=hello%20world&lang=en
6}

The urlQueryAllowed character set permits characters that are valid in the query portion of a URL while encoding everything else. Swift provides several built-in character sets for different URL components.

Character Sets for Different URL Parts

Swift's CharacterSet provides four URL-specific sets, each tailored to a different component of a URL.

swift
1// Characters allowed in the URL host
2CharacterSet.urlHostAllowed
3
4// Characters allowed in the URL path
5CharacterSet.urlPathAllowed
6
7// Characters allowed in the query string
8CharacterSet.urlQueryAllowed
9
10// Characters allowed in the fragment
11CharacterSet.urlFragmentAllowed

Choosing the wrong set is a frequent source of bugs. For example, urlQueryAllowed permits & and = because those are structural characters in query strings. But if your query parameter value itself contains &, you need a more restrictive set.

Encoding Query Parameter Values Safely

When encoding individual parameter values, you should exclude the structural query characters (&, =, +) so they get encoded.

swift
1func encodeQueryValue(_ value: String) -> String? {
2    var allowed = CharacterSet.urlQueryAllowed
3    allowed.remove(charactersIn: "&=+")
4    return value.addingPercentEncoding(withAllowedCharacters: allowed)
5}
6
7let searchTerm = "price>100 & color=red"
8if let encoded = encodeQueryValue(searchTerm) {
9    print(encoded)
10    // price%3E100%20%26%20color%3Dred
11}

This ensures that a value containing & or = does not break the query string structure.

Building URLs with URLComponents

For constructing URLs from parts, URLComponents is safer than manual string concatenation because it handles encoding automatically.

swift
1var components = URLComponents()
2components.scheme = "https"
3components.host = "api.example.com"
4components.path = "/search"
5components.queryItems = [
6    URLQueryItem(name: "query", value: "hello world"),
7    URLQueryItem(name: "category", value: "books & media"),
8    URLQueryItem(name: "page", value: "1")
9]
10
11if let url = components.url {
12    print(url.absoluteString)
13    // https://api.example.com/search?query=hello%20world&category=books%20%26%20media&page=1
14}

URLComponents encodes each query item's name and value individually, which prevents the issues that come from encoding an entire URL string at once.

Encoding Path Segments

Path components also need encoding when they contain special characters. For example, a username used as a path segment might contain spaces or unicode characters.

swift
1let username = "john doe"
2
3var components = URLComponents()
4components.scheme = "https"
5components.host = "api.example.com"
6components.path = "/users/\(username)/profile"
7
8if let url = components.url {
9    print(url.absoluteString)
10    // https://api.example.com/users/john%20doe/profile
11}

If you build the path manually instead of using URLComponents, use addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) on the dynamic segment.

swift
1let username = "john doe"
2if let encodedName = username.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
3    let urlString = "https://api.example.com/users/\(encodedName)/profile"
4    print(urlString)
5}

Decoding Percent-Encoded Strings

To reverse the encoding, use removingPercentEncoding.

swift
1let encoded = "hello%20world%26more"
2if let decoded = encoded.removingPercentEncoding {
3    print(decoded)
4    // hello world&more
5}

This returns nil if the string contains an invalid percent-encoding sequence, so always handle the optional.

Common Pitfalls

Double encoding. If you call addingPercentEncoding on a string that is already encoded, the % characters themselves get encoded to %25. For example, %20 becomes %2520. Always encode raw values, not already-encoded strings.

Using urlQueryAllowed on parameter values. This set allows & and =, which means parameter values containing those characters will break the query structure. Remove those characters from the allowed set when encoding individual values.

Encoding the entire URL at once. If you encode a complete URL string like https://example.com/path?key=value, the ://, /, ?, and = characters may or may not survive depending on the character set you use. Always encode individual components, or use URLComponents to build the URL.

Assuming + means space. In form-encoded data (application/x-www-form-urlencoded), + represents a space. But in standard percent encoding, + is a literal plus sign and space is %20. If your server expects form encoding, you may need to replace %20 with + after encoding.

Ignoring non-ASCII characters. Swift strings are Unicode, and addingPercentEncoding handles multi-byte characters correctly by encoding each UTF-8 byte. However, if you manually build encoding logic, you must handle the full UTF-8 byte sequence for characters outside the ASCII range.

Summary

Swift provides two main approaches to URL encoding. For simple cases, use addingPercentEncoding(withAllowedCharacters:) with the appropriate CharacterSet for the URL component you are encoding. For building URLs from parts, use URLComponents with URLQueryItem, which handles encoding automatically and avoids the structural-character pitfalls of manual string encoding. Always encode individual components rather than full URL strings, and watch for double-encoding when working with values that may already be percent-encoded.


Course illustration
Course illustration

All Rights Reserved.