en
de

What I Like in Swift — And What I Do Not

9 July 2014
| |
Reading time: 9 minutes

Swift is a hot topic at the moment, isn’t it? I was three weeks ago at the Scala Days conference in Berlin — excellent conf by the way and more than happy having met so many great people there — and Swift was, more often than not, a hot conversation topic. “What do you think about Swift?” was I asked. “Is it not similar to Scala?”. It is. And it is not. It’s a different language targeted at a different platform, mobile and desktop client apps rather than server development, and which has a particular relation to its ancestor language, Objective-C, a particularity that other languages will not have. Let’s have a closer look at what I like with this new language, and what I don’t. But I must say that I am somewhat biased with that respect, since I am pretty enthusiast about this new language from Cupertino.

What I Like

Strong Typing

I am personally a fan of strongly typed languages. It allows to catch lots of type errors early and is exponentially useful as the size of a project increases (think about refactoring). A type mismatch is a compile error in Swift. Calling an inexistent method as well.

let a: Int = 5
let b: String = a // compile error
func printIt(msg: String) 
{
    println(msg)
}

printItTypo("hello") // compile error

Type Inference

Being a fan of strong typing, I am also a huge fan of script syntax like Ruby, Groovy or compact programming languages like Scala or O’Caml. While the first two have no proper type inference, they have a very compact and nice syntax. Scala and O’Caml are two functional languages — Scala being also object oriented — and as such they have a super compact syntax as well and use heavily type inference. The good news is: Swift takes also greatly advantage of type inference, to about the same extent than Scala, that means types for variables can be left out most of the time, excepted within method signatures. It gives the beautifully compact syntax we know of script languages while being statically typed. How cool is that?

let nb  = 5
let str = nb
printIt(str) // compile error
let array = ["some", "array"]
printIt(array) // compile error

So for instance in the previous code snippet, we have left the types out, yet each variable’s type is still known to the compiler. On line 2 the variable ‘str’ is of type Int (inferred from the ‘nb’ declaration on line 1), hence line 3 will not compile — since the function printIt from our previous example expects a String parameter. On line 5 we define an array of String, using an array literal, hence line 6 will give us a compile error. Love that!

Optional and Non-Nullable Types

Optionals are a great way to include, in the type system, the information that a variable is allowed to not hold a valid reference or that a given function or method is allowed not to return anything. That makes it crystal clear. “What if this method returns null?”. Won’t happen. The answer to the previous question becomes “what is the method return type?”. Is it an option type? If yes, then the method is allowed to return nothing, otherwise not. Period. Functional languages have support for option types and so does Scala. Swift goes one step beyond what is done in that respect in Scala, which recommends never to return ‘Some(null)’, but you can basically do it if you want. There is no guarantee that it won’t happen — though it is a very bad practice. Swift forbids it. A non-optional type in Swift is not nullable. Similarly, a method which might return ‘nil’, must have an optional return type. Let’s have a look at the code:

let a: String? = nil
let b: String = a // compile error
let c = "54"
let d: Int? = c.toInt()
let e = d + 1 // compile error
let f = d! + 1

What happens here? The variable ‘a’ is of optional string type — I am explicitly writing the type to make it clear. On line 2 we get a compile error, since we declare b of non-optional String type. If I would have left out the type of b, it would have been inferred, as expected, to optional String type. The method ‘toInt()’ on String returns a result of type ‘Int?’ (optional Int), since the string might not be a valid integer representation and hence the result might be ‘nil’. We therefore get a compile error on line 5 since we cannot add 1 directly to an optional Int, we need to unwrap it first, like on line 6.

Pattern Matching

This is a feature I love in functional languages and in Scala. It allows to describe part of an algorithm in a very clean and descriptive way. And Swift has this feature — to a certain but fair extent. Let’s look at an example.

func welcomeMessage(name: String, age: Int) -> String? {
    switch (name, age) {
    case (_, let a) where a <= 0: return nil
    case (_, 0..<18): return "not grown up"
    case ("john", 18): return "sorry john, not allowed"
    case (let n, _): return "hi \(n), welcome!"
    }
}

assert(welcomeMessage("alan", -3) == nil)
assert(welcomeMessage("hans", 5)! == "not grown up")
assert(welcomeMessage("john", 18)! == "sorry john, not allowed")
assert(welcomeMessage("albert", 24)! == "hi albert, welcome!")

We match here on the tuple (name, age). In the first case, line 3, we don’t care about the name and return no result, since the age is not valid. In the second case, we still don’t care about the name and match the open interval 0 to 18 (18 is not included, ‘0…18’ would include 18 as well) — note the optional return type and the unwrap operator ‘!’ on line 11. In the third case we match exactly one possible value of (name, age). And finally in the last case we match all the remaining cases and assign the name to the constant n. While not as powerful as in Scala, the pattern matching in Switch is a lot more versatile than in Java (or Groovy).

In Swift we can:

  • Match any enum members, working also nested
  • Match a String value
  • Match a range of integer values
  • Use the underscore notation if we don’t care about a given value
  • Make an assignment with ‘let’
  • Express an additional condition with ‘where’ — similar to Scala with ‘if’

What we cannot do with pattern matching

  • There is no deconstructor for the array type, hence we cannot match the first item of an array and the rest.
  • Since algebraic data types with recursive values (think trees or expressions) cannot be defined yet, we cannot use the power of pattern matching to implement algorithms on them.

Constant and Variable Definition

The constant declaration is very straightforward with ‘let’ and defining a mutable variable with ‘var’. This is similar to Scala for instance (except that the constant declaration is done there with ‘val’). So far in this post I have defined only constants. Let’s define another constant and a mutable variable.

let a = "I am a constant"
a = "we cannot redefine a" // compile error
var b = "I am a mutable var"
b = "this is allowed"

Built-in Enums and Algebraic Data-Types

In Objective-C there was at least 3 or 4 ways to define the equivalent of an enum — the hacky way with the ‘#define’ macro, using the enum keyword, with a typedef modifier, with and without an NS_ENUM macro. You’re welcome! I won’t miss this plethora of choices. Swift allows a modern enum syntax — with or without an underlying raw value.

enum SomeEnum {
    case Foo
    case Bar
}

enum OtherEnum: Int {
    case Foo = 1
    case Bar = 2
}
assert (OtherEnum.Foo.toRaw() == 1)

The nice thing with enums is that you can build algebraic data types with them, each enum member being associated with a different set of types. The only problem is that algebraic data types become interesting when such an associated value can refer recursively to the whole type, allowing to build recursive structures like lists, trees or expressions. Currently the Swift REPL crashes, as of Xcode 6 Beta 3, if we enter such a recursive enum, hence we don’t know if it is a bug that will be fixed until the public release, or if such enum definitions are simply out of scope for the moment. Such an algebraic data type would look like the following snippet:

// makes the Swift REPL and playground crash
enum Expression {
    case Number(Double)
    case Add(Expression, Expression)
    case Sub(Expression, Expression)
}

Of course such an expression could also use Generics to be type independent and have more than two operation nodes.

String Interpolation

It sounds like relatively evident for a modern programming language and so it is logical that Swift has this feature. Very handy.

let name = "john"
println("hi \(name), what's up?")
let c = 25
println("result is \(c * 2)")

Closures

We have in Swift a good support for closures. They are first class citizens — we can pass them as parameters and return them from functions. There is also a good API for using them to map or reduce values within collections.

["hi", "hello", "world"]
    .filter{s in s.hasPrefix("h")}
    .map {s in countElements(s)}
    .reduce(0) {sum, len_i in sum + len_i}

This example selects the words from the list beginning with an ‘h’, map each one to its length, and compute their sum — with a start value of 0.

Two notes in addition regarding closures:

  • Although we don’t specify the parameter types, thank to type inference the compiler does his type checking homework — hence we cannot call a non existing method on a given parameter.
  • Like in groovy, if a closure is the last parameter of a method, we can put it after the closing parameter bracket, e.g. ‘foo() { /* some closure */ }’. That allows for an elegant syntax (giving the impression that we define new keywords).

Compatibility with Previous iOS & OS X Versions

Swift is compiled to machine code and is compatible with the previous iOS version 7 and OS X Mavericks, as long as Xcode 6 is used.

REPL and Playgrounds

Like many other modern programming languages — Scala, O’Caml, Ruby, Python, etc., Swift is shipped with a REPL — read eval print loop (or interactive prompt). Very handy to play with the language and try out little code snippets while programming. To start it, just type ‘swift’ in the command line terminal — given you have Xcode 6 installed. To do even more complicated interactive executions there are the Playgrounds. They allow to type code — including protocol & class definition — and get in a second column the result of each line. Modify a line and get instantaneously the new result showing up. There are even advanced features like visualising colors, images, and getting a graph of the evolution of some variable value during code execution.

Nested Comments

I know what you are thinking. Are nested comments such a big deal? They are not, but it shows that the language is well defined — and that its creator knows how to write a proper parser. It basically allows to comment out a code part where comments are already present.

/*
/* doing some stuff */
let stuff = "some"
*/

Miscellaneous

What I have mentioned so far is just a small portion of what is possible with Swift. There are lots of other features that I think are cool. Among others:

  • Extension of existing classes (aka categories)
  • Good integration with existing Objective-C code
  • Tuples of values (we have seen them in the paragraph on pattern matching above)
  • Generics

And What I Don’t Like

Array Immutability

Ok, this one got fixed in XCode 6 Beta 3 (released on Monday, Juli 7th). The behavior of an immutable array was previously very close to a final array in Java — existing elements could be nevertheless modified. It is not the case anymore as of Beta 3. Now arrays have a consistent syntax with respect to immutability, an array declared with ‘let’ is not mutable (including existing elements) while one declared with ‘var’ is mutable (existing elements can be modified and new elements can be inserted or existing ones removed). Exactly what I was expecting.

let array1 = [1,2,3]

array1[0] = 3 // compile error
array1 += 4 // compile error

var array2 = [1,2,3]
array2[0] = 3
array2 += 4
assert(array2 == [3,2,3,4])

Array Deconstructor for Pattern Matching

As already mentioned above under pattern matching, it is not possible to match the head and tail of a list and process the list recursively.

Operator Overloading Globally Defined

Operator overloading is possible in Swift, although you don’t define the method in the class of the first type — like in Groovy or Scala, but globally, which I think is weird. Let’s look at an example.

class Spoon {
    let size:Int
    init(size:Int) {
        self.size = size
    }
}

func + (a: Spoon, b: Spoon) -> Spoon {
    return Spoon(size: a.size + b.size)
}

let s: Spoon = Spoon(size: 10) + Spoon(size: 20)

So we see that the ‘+’ function is defined globally, outside of the Spoon class.

Swift Radars

The interested reader can have a look at a collection of bug reports regarding Swift — also called radars in the Apple terminology.

Note

The code features mentioned in this article are referring to Xcode 6 Beta 3. The Swift programming language is still under heavy development — think about the new syntax for open range “0..<10”, the new array immutability semantic or the new syntactic sugar notation for array and dictionary types (‘[Int]’ for ‘Array<Int>’ and ‘[String,Int]’ for ‘Dictionary<String,Int>’), all introduced in Beta 3. Significant changes might still be made in future beta versions.

Where to Go from Here?

You have a couple of options to learn more about Swift. A good starting point is Apple’s free book ‘The Swift Programming Language’ — available in iBooks. You can also download Xcode 6 — you will need for that to register yourself as an iOS or Mac developer. In Xcode I recommend to play a bit inside a Playground. For that you can create an empty iOS or Mac project, add a new file and choose ‘Playground’ in the category ‘Source’. Let me know what you think in the comment section!

Comments (0)

×

Sign up for our Updates

Sign up now for our updates.

This field is required
This field is required
This field is required

I'm interested in:

Select at least one category
You were signed up successfully.

Receive regular updates from our blog

Subscribe

Or would you like to discuss a potential project with us? Contact us »