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.
Enter a name, tell Xcode where you'd like to save the playground, and hit create.
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.