Working With URLComponents In Swift

At times, it can seem as if Foundation is an old, dusty framework that you have to use to get anything done on iOS, tvOS, macOS, or watchOS. Nothing could be further from the truth, though. Every year, Apple improves the framework and adds new components to better serve developers. In this episode, I'd like to talk about a favorite of mine, the URLComponents structure.

First Things First

Fire up Xcode and create a new playground by choosing the Blank template from the iOS section.

Setting Up the Playground

Enter a name, tell Xcode where you'd like to save the playground, and hit create.

Setting Up the Playground

Remove the contents of the playground and add an import statement for the Foundation framework. Remember that URLComponents is defined in Foundation.

import Foundation

URLs, Parameters, and Fragments

Creating URLs isn't difficult. Take a look at this example. The init?(string:) initializer returns an optional URL instance.

var url = URL(string: "https://myapi.com")

Adding a path to the URL isn't too bad either.

url = url?.appendingPathComponent("users")

But it starts to get messy from the moment you need to add query parameters. The keys and values of the query parameters need to be properly percent encoded, for example. And what happens if you need to append a fragment to the URL?

Even though these issues aren't insurmountable, you may be wondering why working with URLs isn't easier on a platform that has been around for years and years.

Introducing URLComponents

A few years ago, Apple added a type to the Foundation framework that makes working with URLs easier and more elegant, the URLComponents structure in Swift or, if you prefer Objective-C, the NSURLComponents class. Both types are available on iOS 7.0+, tvOS 9.0+, macOS 10.9+, and watchOS 2.0+.

In the remainder of this episode, we focus on the URLComponents structure. The URLComponents structure has two responsibilities, parsing URLs and constructing URLs. It's important to know that the URLComponents structure expects URLs to comply with RFC 3986, a widely used standard.

Let me show you how easy it is to construct a URL instance with the URLComponents structure. We first need to create an instance of the URLComponents structure.

var components = URLComponents()

We then set the scheme and host properties of the instance.

components.scheme = "https"
components.host = "myapi.com"

If we ask the URLComponents instance for the value of its url property, we receive an optional URL instance. The current value is equal to https://myapi.com.

components.url

We can continue to configure the URLComponents instance by setting its queryItems property. This property is of type [URLQueryItem]. The URLQueryItem structure is another lightweight addition to the Foundation framework.

let queryItemToken = URLQueryItem(name: "token", value: "12345")
let queryItemQuery = URLQueryItem(name: "query", value: "swift ios")

components.queryItems = [queryItemToken, queryItemQuery]

The value of the url property of the URLComponents instance is now equal to https://myapi.com?query=swift%20ios&token=12345. Notice that the values of the parameters are automatically percent encoded.

The URLComponents structure also adds support for fragments and authentication. This results in https://bartjacobs:mypassword@myapi.com?token=123456&query=swift%20ios#five.

components.fragment = "five"

components.user = "bartjacobs"
components.password = "mypassword"

The URLComponents structure has many more convenience methods for creating URLs. It can also be used to extract information from URLs. Let me show you how that works.

Dissecting URLs With URLComponents

The URLComponents structure isn't only useful for creating URLs, it's equally sublime for dissecting URLs. Take a look at the following example. Because the init?(url:resolvingAgainstBaseURL:) initializer is failable, we receive an optional URLComponents instance.

let url = URL(string: "https://bartjacobs:mypassword@myapi.com?token=12345&query=swift%20ios#five")!

let components = URLComponents(url: url, resolvingAgainstBaseURL: false)

We instantiate a URLComponents instance by invoking init?(url:resolvingAgainstBaseURL:). This initializer defines two parameters, a URL instance and a boolean value.

The boolean value determines how the URL instance should be evaluated. If a relative URL is passed to the initializer, the value of the second parameter needs to be equal to true to make sure the value of the absoluteURL property is used to extract the components of the URL.

We can now access the various components of the URL we used to create the URLComponents instance.

if let components = components {
    components.host
    components.query
    components.percentEncodedQuery

    if let queryItems = components.queryItems {
        for queryItem in queryItems {
            print("\(queryItem.name): \(queryItem.value)")
        }
    }
}

Strings Work Just as Fine

You can also create a URLComponents instance from a string by invoking the init?(string:) initializer. The initialization fails if the string doesn't comply with RFC 3986.

let components = URLComponents(string: "https://cocoacasts.com")

It's in the Small Things

Foundation is a powerful framework no Cocoa developer can do without. Even though it lacks a few features, Apple continues to fill in the gaps. The DateInterval structure is another welcome addition that makes the Foundation framework that little bit more enjoyable to use. Later this week, I show you the benefits of the DateInterval structure for working with dates and date intervals.