URL Encoding GET parameters in Swift

When creating a GET URLRequest in Swift, it's often necessary to URL encode GET string parameters. Many languages include String functions to emit url encoded versions of Strings, but as of this writing Swift does not fully address this problem in the Foundation String class. However, Swift Foundation actually does have functions to create properly formatted url encoded GET strings, which this post will demonstrate.

What is URL encoding

URL encoding is nothing more than replacing special characters with tags that can be transmitted over the Internet in a web URL using the ASCII character set.

URL encoding replaces characters with hexadecimal digits preceded by the percent character, for example:

// Not URL encoded
hello world & goodbye

// URL encoded
hello%20world%20%26%20goodbye

Approaching the problem with String extensions

When searching for solutions to this problem, often the answer on StackOverflow is to "roll your own" String extension to url encode strings. For example:

public extension String {
    func urlEncoded() -> String? {
        addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
    }
}

and then using this function to URL encode get parameters:

let headline = "Tell the world \"hello & goodbye\""
let queryParameters = "?headline=\(headline.urlEncoded())"
// queryParameters == "?headline=Tell%20the%20world%20%22hello%20&%20goodbye%22"

This approach is "technically correct" because the ampersand character is allowed in a url path.  But for this scenario it isn't correct, because the resulting hello & goodbye string has an ampersand embedded in the output: hello%20&%20goodbye. In a GET string, ampersands are interpreted as parameter delimiters, so the web server will not realize that hello & goodbye is a single value. The headline parameter's value will be incorrect when read by the server.

The correct solution

Swift Foundation includes a purpose-built (but lesser known) solution that accurately solves URL parameter encoding: URLQueryItem and URLComponents.  These classes can actually do more than just build URL encoded GET strings, so it's worthwhile to peruse the documentation here to learn all they can do.

To solve the GET string encoding problem, simply use these classes to explicitly define parameters and let URLComponents take care of the encoding:


var queryItems = [URLQueryItem]()
queryItems.append(URLQueryItem(name: "headline", value: headline))

var components = URLComponents()
components.queryItems = queryItems
let queryParameters = components.string

// queryParameters == "?headline=Tell%20the%20world%20%22hello%20%26%20goodbye%22"

This time the ampersand within the headline has been correctly encoded as %26, and will not be misinterpreted as a parameter delimiter by the backend web service.