Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

Apple goes to the Swift

Jerry Stratton, June 7, 2014

photo for Swift Apple

The race may not always go to the swift, but that is the way to place your bets.

The most exciting part of the WWDC keynote last Monday wasn‘t the new operating systems for the Macintosh and iDevices. It was the announcement of the new Swift programming language for MacOS and iOS. A new programming language is my equivalent of “one more thing…”

It took a few seconds for the Swift Language Guide to appear on the iBookstore after the announcement, but the reference makes me even more excited. Swift appears to combine some of the best features of Objective-C/C++ and Python.1

The book, which is also available online, hooked me. I have not yet programmed in Swift, but I’m going to, and soon. I dabbled in iOS app creation for work, but so far all of my personal iPhone/iPad apps have been XHTML5. Swift makes me want to find some use for it.

It’s pretty rare for me to read a programming book all the way through; good ones, like the Swift book, make me want to hit the keyboard. In this case, even though I don’t have access to my iMac, I still wanted to, and did, finish the guide.

Swift will definitely be familiar to Python programmers. For example, it contains the for…in control statement that I first saw in Python.

In addition to the traditional for-condition-increment loop found in C, Swift adds a for-in loop that makes it easy to iterate over arrays, dictionaries, ranges, strings, and other sequences.

Swift contains both a while and a while… do loop, as does C++. It also includes C++’s switch statement, however, there are major differences:

The cases of a switch statement do not “fall through” to the next case in Swift, avoiding common C errors caused by missing break statements.

You can specify that any particular case does fall through, but that’s not the default.

A switch statement must handle all possible cases or the code will not compile. This is especially useful for enumeration types.

…a switch statement must be exhaustive when considering an enumeration’s members. If the case for .West is omitted, this code does not compile, because it does not consider the complete list of CompassPoint members. Requiring exhaustiveness ensures that enumeration members are not accidentally omitted.

Similarly, functions that return a value must always return a value; a function cannot sometimes return a value and sometimes just end.

Return values can be ignored, but a function that says it will return a value must always do so. A function with a defined return type cannot allow control to fall out of the bottom of the function without returning a value, and attempting to do so will result in a compile-time error.

Safety first

A lot of Swift appears to be about avoiding common mistakes that result in errors or security issues in programming complex (or even simple) apps. Variables and constants must be given a specific type, as in C++, but this is easier than in C++ because if a variable is given a default value, the type of the value is the type of the variable or constant.

This follows through even to the members of arrays.

Swift arrays are specific about the kinds of values they can store… If you create an array of Int values, for example, you can’t insert any value other than Int values into that array. Swift arrays are type safe, and are always clear about what they may contain.

Normally, an array is designed to contain specific types of values; Swift makes this explicit, and compile-time or run-time errors will result if something else is accidentally added.

This does not mean that you can’t have mixed-type arrays, just that the reason needs to be explicit. For example, protocols can be added to classes to require that they include specific functionality, and the array can be explicitly tied to a protocol; the requirement will then be that any member of the array must implement that protocol.

And when overriding methods and properties in subclasses, the override keyword is required, to ensure that the programmer knows that an override is occurring.

Variables/properties must contain values; not containing a value is an error. If a variable might not contain a value, that variable must be marked as allowing optional values. For example, a variable of type Int must contain an integer. A variable of type Int? will contain an integer or nothing, and the programmer must check whether the variable contains something before using the variable as an integer.

This behavior allows the programmer to anticipate that some properties in a long chain might not exist. Swift calls this optional chaining.

[toggle code]

  • if let roomCount = john.residence?.numberOfRooms {
    • println("John's residence has \(roomCount) room(s).")
  • } else {
    • println("Unable to retrieve the number of rooms.")
  • }

This tells Swift to “chain” on the optional residence property and to retrieve the value of numberOfRooms if residence exists.

Parameters

Another Python-like feature is named parameters when calling functions. Any parameter that has a default value is also a named parameter. Python programmers can also eschew semicolons: Swift only needs semicolons when multiple statements are combined in a single line; otherwise, semicolons are ignored. And, as in Python but not C++, classes are not defined by which file they are in; a single file can contain multiple class definitions as well as other code.

Values, references, and laziness

Swift defines value types and reference types. Value types are copied when assigned to new variables or given as parameters to functions; reference types are passed by reference in each case.

…all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes.

All structures and enumerations are value types in Swift. This means that any structure and enumeration instances you create—and any value types they have as properties—are always copied when they are passed around.

Classes, on the other hand, are reference types, and are passed by reference, so that, unless a deep copy is made, changes to one reference are reflected in other references. If you need to know whether two references refer to the same instance,

Swift also provides two identity operators (=== and !==), which you use to test whether two object references both refer to the same single instance.

For arrays, there’s a bit of Django-like magic behind the scenes to keep things moving quickly:

For arrays, copying only takes place when you perform an action that has the potential to modify the length of the array. This includes appending, inserting, or removing items, or using a ranged subscript to replace a range of items in the array.

You can specifically remove this behind-the-scenes behavior by using the unshare method on one of the arrays to ensure that it is a copy and not a shared version.

This laziness is also available for your own properties using what looks a lot like a Python decorator.

Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete. Lazy properties are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.

[toggle code]

  • class DataImporter {
    • //expensive code for importing data
  • }
  • class DataManager {
    • @lazy var importer = DataImporter()
  • var data = String[]()
  • }

The importer property will only create a DataImporter instance if some other code makes use of it. Otherwise, no DataImporter instance is created.

Class-like behavior

Everything in Swift has class-like behaviors, often including methods and properties, and can be extended like classes. Arrays, for example:

  • array.isEmpty
  • array.append(value)
  • array += value
  • array3 = array1 + array2

Even basic properties can be extended. For example, observers can be set on properties, and the observers will be executed when the property changes:

If you implement a willSet observer, it is passed the new property value as a constant parameter. You can specify a name for this parameter as part of your willSet implementation. If you choose not to write the parameter name and parentheses within your implementation, the parameter will still be made available with a default parameter name of newValue.

Similarly, if you implement a didSet observer, it will be passed a constant parameter containing the old property value. You can name the parameter if you wish, or use the default parameter name of oldValue.

This behavior is also available to global and local variables.

Classes, structures, and enumerations can all contain methods. This makes them a lot closer to each other than in Objective-C. For example, a structure can be given subscript behavior:

[toggle code]

  • struct TimesTable {
    • let multiplier: Int
    • subscript(index: Int) -> Int {
      • return multiplier * index
    • }
  • }
  • let threeTimesTable = TimesTable(multiplier: 3)
  • println("six times three is \(threeTimesTable[6] )")
  • // prints "six times three is 18”

Structures can also be given initializers:

[toggle code]

  • struct Celsius {
    • var temperatureInCelsius: Double = 0.0
    • init(fromFahrenheit fahrenheit: Double) {
      • temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    • }
    • init(fromKelvin kelvin: Double) {
      • temperatureInCelsius = kelvin - 273.15
    • }
  • }

Both classes and structures can:

  • Define properties to store values
  • Define methods to provide functionality
  • Define subscripts to provide access to their values using subscript syntax
  • Define initializers to set up their initial state
  • Be extended to expand their functionality beyond a default implementation
  • Conform to protocols to provide standard functionality of a certain kind

Classes also have the following characteristics:

  • Inheritance enables one class to inherit the characteristics of another.
  • Type casting enables you to check and interpret the type of a class instance at runtime.
  • enable an instance of a class to free up any resources it has assigned.
  • Reference counting allows more than one reference to a class instance.

Just as the built-in array type has defined special meanings for the basic operators such as addition, these operators can be overridden for custom classes and structures:

[toggle code]

  • struct Vector2D {
    • var x = 0.0, y = 0.0
  • }
  • @infix func + (left: Vector2D, right: Vector2D) -> Vector2D {
    • return Vector2D(x: left.x + right.x, y: left.y + right.y)
  • }

This creates a meaning for adding two Vector2D values together. You can also create your own custom operators.

Custom operators can be defined only with the characters / = - + * % < > ! & | ^ . ~.

  • operator prefix +++ {}

For infix operators, you can also provide custom precedence and associativity.

Cocoa integration

One disappointing aspect of the Swift guide is that there is very little talk of how it integrates with the Mac OS and iOS. However, what is mentioned indicates that the integration is close and well-designed.

Swift’s String type is bridged seamlessly to Foundation’s NSString class. If you are working with the Foundation framework in Cocoa or Cocoa Touch, the entire NSString API is available to call on any String value you create, in addition to the String features described in this chapter. You can also use a String value with any API that requires an NSString instance.

There is more at the web site. In Objective-C, a table view might look like this:

  • UITableView *myTableView =[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];

In Swift it will look like:

  • let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)

Swift handles the allocation and type definition, and shortens the code using dot notation. Similarly, dot notation makes using Objective-C objects more readable, too.

[toggle code]

  • myTextField.textColor = UIColor.darkGrayColor()
  • myTextField.text = "Hello world"
  • if myTextField.editing {
    • myTextField.editing = false
  • }

And it is also easy to extend Objective-C classes:

[toggle code]

  • import UIKit
  • class MySwiftViewController: UIViewController {
    • // define the class
  • }

There is a lot of “information forthcoming” in the Cocoa/Objective-C integration documentation, but all-in-all it looks like it makes app creation a lot easier on both the Mac and iOS.

One odd typo?

There are a couple of odd typos in the current version of the guide. If not typos, then I don’t understand how the code works, so I’m including it here as a caveat. Note that I have not run any of this code, and one of the advantages of Swift is the ability to run code semi-interactively. I just don’t have a Mac available right now.2

[toggle code]

  • class AutomaticCar: Car {
    • var gear = 1
    • override var speed: Double {
      • didSet {
        • gear = Int(speed / 10.0) + 1
      • }
    • }
    • override func description() -> String {
      • return super.description() + " in gear \(gear)"
    • }
  • }

According to the guide,

Specifically, the property observer chooses a gear which is the new speed value divided by 10, rounded down to the nearest integer, plus 1. A speed of 10.0 produces a gear of 1, and a speed of 35.0 produces a gear of 4…

I’m pretty sure that a speed of 10.0 will produce a gear of 2, but I have always had problems shifting gears at liminal speeds.

  1. Most likely directly, given Mac OS X’s reliance on Python, although it could also be pulling Python-like features from a common relation.

  2. I am aching to get back and try this stuff!

  1. <- Timeout retry
  2. iOS BASIC ->