Let’s create a new playground, and
import Foundation
at the top.
import Foundation
As we've hinted at previously,
import
ing a module makes its type available in the current file. Unlike a C
#include
compiler directive, there are no header sources copied into the current file. Instead, each module defines what is exports, which we'll cover another week.
URL’s
First, we'll cover
URL
's.
var url:URL? = URL(string:"https://www.apple.com")
//https://www.apple.com
The first thing to notice is that the
URL
init(string:
method does not guarantee it will create a
URL
from the
String
. The
String
must be correctly-formatted in order for this to work. Let’s see it fail, by making the url invalid, by using a space character:
var url:URL? = URL(string:" ") //nil
This means in code, you cannot assume that creating a
URL
will succeed, so please don’t force-unwrap the optional. However, it does mean that if you require a
URL
in your API, it means validation will have already taken place.
That works for creating a
URL
to the internet.
var url:URL = URL(fileURLWithPath:"/users/ben/Documents/Swift 14 Foundation.md")
For
URL
’s to files need to be constructed specially if you already have a path
String
:
Notice that we’re making a pretty big deal out of the fact that one is a file and the other is not.
var url:URL? = URL(string:"file:///users/ben/Documents/Swift%2014%20Foundation.md")
You can create a file URL with the
string:
init function, but you’ll need to make sure you include the correct scheme, leave out the domain, and convert the spaces to precent escape encoding, so in other words, if you need to take a string path from another API, use the
URL(fileURLWithPath:
init
function to convert.
There is a property on the
URL
,
.isFileURL
, which will directly let you know if a
URL
is actually a file. This is preferred over, for instance, using
.hasPrefix("file:///")
on a
String
.
var isAFile:Bool = stringURL.hasPrefix("file:///") //anti-pattern
var isAFile:Bool = url.isFileURL // thumbsup
For working with paths,
URL
knows how to pull out path components into an array:
var pathComponents:[String]? = URL(string:"https://developer.apple.com/wwdc/live")?.pathComponents
// ["/", "wwdc", "live"]
Or read the extension:
var fileExtension:String? = URL(fileURLWithPath:"/video.mp4").pathExtension
It also knows how to modify a url to add more path components:
var urlWithPath:URL? = URL(string:"https://developer.apple.com/wwdc/live")
try! urlWithPath?.appendPathComponent("slides")
urlWithPath //https://developer.apple.com/wwdc/live/slides
Many platforms rely on the use of strings to refer to files or internet resources, but
Foundation
provides a custom value type,
URL
. When importing a String from another source that should be represented with a URL, convert them immediately, leaving the interior of your code to work with this canonical type.
URLComponents - a Builder pattern
var scheme:String? = url.scheme
var host:String? = url.host
var query:String? = url.query
The
URL
struct does provide read-only properties for extracting the pieces of a
URL
, like
scheme
,
host
, and
query
in this example. But they don’t allow direct setting, so let’s take a look at a companion struct,
URLComponents
.
var components = URLComponents()
components.scheme = "https"
components.host = "developer.apple.com"
components.path = "/wwdc/live"
components.user = "jonny"
components.password = "1P40n3"
components.queryItems = [URLQueryItem(name: "skip", value: "20"),
URLQueryItem(name: "search", value: "Best Practices")]
URLComponents
is what’s known as a "Builder" pattern. It’s an object whose properties are configured one at a time, and then it produces a manufactured object, a
URL
. This lets us forget about formatting, and set each field directly.
let url:URL = components.url
//https://jonny:1P40n3@smoothterminal.com/wwdc/live?skip=20&search=Best%20Practices
Then let the components type take care of creating a correctly-formatted
URL
for me:
Notice that the query parameters automatically got percent escaping for the right characters. Also notice that the query items are an
Array
, not a
Dictionary
, since the URL spec does not prevent a query key from appearing more than once.
One might even imagine that instead of passing
URL
’s through various layers of an app, each one tacking on different fields with very carefully calculated string offsets, only the
URLComponents
would be passed around, with each layer of the app modifying it to its heart’s content, only to be turned into a perfectly formatted
URL
at the very end. In short, this is a grand slam for value types in Swift over the Class / Mutable Class Obj-C architecture.
Dates
Foundation
provides basic date/time-types. There is a
Date
, which represents an absolute position in time:
let now:Date = Date()
The empty initializer gives us "now".
We can also construct a
Date
with a Unix system time:
let someSpecificTime = Date(timeIntervalSince1970:4567.0)
By using the word
since
, the
init
method’s intention is to make
Date
s after 1970 have a positive value, and
Date
s before negative.
Similarly, we construct a date moments into the future using:
let nearFuture = Date(timeIntervalSinceNow:0.4)
We can compute the
TimeInterval
between two
Date
s, which is simply a
typealias
of a
Float64
, using
now.timeIntervalSince(Date(timeIntervalSinceNow: 10.0))
//-10.004
You’ll notice you don’t quite get a perfect "10.0" because it takes milliseconds for the playground to move between these computations.
And again, the values are positive if the receiver is after the argument date, negative if it is before the argument date.
Alternatively, if we need to add small time intervals to a given date, Date provides simple math operations:
Date can also add small time intervals:
var nearFuture = now.addingTimeInterval(0.4)
nearFuture.addTimeInterval(0.2)
I recommend you pretty much never do this, however, because a
Date
is just a time interval since 1970. Since clocks are constantly changing to try to adjust for various political time changes and minute variations in the inaccuracy of our conventions, like 365-day years, or 24-hour days, merely adding time intervals to dates will not always get you what you want.
Just as we had a
URL
to represent a single fully-composed URL, and used
URLComponents
to create them, we’ll use
DateComponents
to build
Date
s.
var components = DateComponents()
components.year = 2016
components.month = 3
components.day = 1
components.hour = 17
components.minute = 32
components.second = 12
components.calendar = Calendar.init(identifier: .gregorian)
components.timeZone = TimeZone.current
let date = components.date //"Mar 1, 2016, 5:32 PM"
This constructed a Date for us, without our having to do any math, or know anything about leap years. Notice that we needed to get a
Calendar
to make the components work. This is necessary, because although a single point in time represented by a
Date
is the same in each
Calendar
system, the way to break down the years, months, days, etc… are not the same.
Unlike arrays which use 0-based indexes,
DateComponents
is 1-based. In other words, Jan 1, has
.month = 1
.day = 1
This means that we won’t need to alter any numeric values from when converting from the user’s writing or to display.
`DateComponents` are also useful in performing math operations on `Date`s.
var difference = DateComponents()
difference.day = -1
let future = gregorianCalender.date(byAdding: difference, to: date)
//"Feb 29, 2016, 5:32 PM"
Here, we create a blank set of
DateComponents
, then set a -1 for the
.day
. Then we ask the calendar to perform the math, giving us a new
Date
. Without needing to know anything about leap years, Foundation has provided us with the correct date before. In general, using components and Calendar-based math in this way helps us entirely avoid Date math problems, like leap years, and daylight savings time.
Summary
Today we learned the Foundation module, which ships with Swift, has many standard types used for building representing real values. Among those are
URL
s, which represent both files and web resources, and
Date
s which represent moments in time. Both have a
Components
companion type which provide a "builder" pattern. The components provide direct meaningful access to the meaningful pieces of the larger types, and assemble one correctly on demand.
A standard library with value types for representing real-world values, now that's Swift!
Comments for Common Foundation Types