en
de

What I Like in Swift 2 — The Return (Part 1 of 3)

8 July 2015
| |
Reading time: 5 minutes

About one year ago I wrote this blog post about the newly announced Swift programming language from Cupertino. At the time I was pretty excited by the pure amount of beautiful programming constructs present in the language. One year later, Swift 2 is announced and I become compelled again to deliver a truckload of ephemeral yet at some level probably useful thoughts on the second main iteration of the language. Sit tight, there is definitely a huge number of new features which are worth mentioning.

This is part 1 of 3 of this post series about Swift 2. Today we will discuss the following topics:

  • Nullability of optionals
  • Multiple let clauses in a single if statement
  • Extension to pattern matching for if and for
  • Algebraic data types
  • map, filter & co as methods

First things first, I will begin to recap two important features from Swift 1.2, released about 3 months ago, which had a certain exposure during the WWDC this year.

Optionals

We probably all agree that optionals are cool, provided they are used wisely. In Swift up to version 1.1, every API method which could return an object of some type did return an optional, since the reference could be nil. That was not precisely a wise usage since the code using such an API was unnecessary complicated with lots of nil checking if let … statements all over the place.

Swift 1.2 brought a solution for this problem: it made possible to define in Objective-C if a reference is allowed to be nullable or not. The auto-translated Swift method would then accept or return a non-optional type if the Objective-C reference was non-nullable.

Here would be the Objective-C header.

@interface MyClass : NSObject

- (nullable NSString*)toString1:(NSInteger)value;
- (nonnull  NSString*)toString2:(NSInteger)value;
- (null_unspecified NSString*)toString3:(NSInteger)value;

@end

And here the auto-generated Swift interface, hence the way how MyClass is visible to other Swift entities:

class MyClass : NSObject {
    func toString1(value: Int) -> String?
    func toString2(value: Int) -> String
    func toString3(value: Int) -> String!
}

Notice how the return type optionality in the Swift interface has been derived from the nullability we defined in Objective-C. In addition to an optional or non-optional reference, we have the case when the nullability is not known (i.e. if we do not specify anything or if we use the keyword null_unspecified) where we get an implicitly unwrapped reference in Swift (third method).

 

More info: WWDC talk Swift and Objective-C Interoperability

Multiple let Clauses in an if

Still a Swift 1.2 functionality. Chaining the let clauses inside the if statement makes the code more readable. It avoids the so-called “pyramid of doom”, to retake the terminology used during the WWDC.

Previously until Swift 1.1, if you had a bunch of if let … statements to execute, you did end up with the following kind code.

// Swift 1.1: pyramid of doom
let a: Int? = 5, b: Int? = 6, name: String? = "john"
if let a = a {
    if let b = b {
        if let name = name {
            print("do smth with \(a), \(b) and \(name)")
        }
    }
}

That is definitely not super readable for some quite trivial code. From Swift 1.2 we can chain the let statements inside the same if. A lot cooler.

// Swift 1.2: let chaining inside an if
let a: Int? = 5, b: Int? = 6, name: String? = "john"
if  let a = a,
    let b = b,
    let name = name {
        print("do smth with \(a), \(b) and \(name)")
}

We can even add an additional condition to the if using a where clause.

// Add an additional where clause to the if
if  let a = a,
    let b = b,
    let name = name
    where a < b && name.hasPrefix("jo") {
        print("do smth with \(a), \(b) and \(name)")
}

In a subsequent let, you can of course access a variable defined in a previous let from the same if clause.

More info: WWDC talk ’What’s new in Swift’.

We are now good to talk about Swift 2 features. At your place I would go grab a coffee at this time, since we have a couple of things to talk about here.

Pattern Matching

We have a few cool new language features regarding pattern matching.

Firstly Pattern matching is now accepted within a for … in … loop. In this case only the elements from the collection satisfying the case expression will be iterated over.

enum MyType {
    case Nb(Int)
    case Str(String)
    case Unknown
}

let coll: [MyType] = [.Nb(1), 
                      .Nb(2), 
                      .Str("john"), 
                      .Unknown]
for case let MyType.Str(s) in coll {
    print(s)
}

// will print out only "john"

Secondly it is possible to include a case statement within if, in which case the if will be fulfilled only if the case pattern holds.

let nbs = [5,42,442]

for nb in nbs {
    if case 1...10 = nb {
        print("\(nb) in [1,10]")
    }
    else if case 100...1000 = nb {
        print("\(nb) in [100,1000]")
    }
    else {
        print("\(nb) not in a defined interval")
    }
}

// prints out:
// “5 in [1,10]"
// “42 not in a defined interval"
// “442 in [100,1000]"

Note: normally such a pattern matching within a loop would be done directly using a switch statement, we want here to illustrate how if case is working though.

The syntax with the single equality sign if case 1..10 = nb is definitely weird, since normally a single = is used for assignment and not for comparison. Go figure.

More info: WWDC talk ’What’s New in Swift’.

Algebraic Data Types

That is probably not a critical addition to the language, nice to have though. I reported one year ago that algebraic data types with recursive type references where not supported. They are apparently now, by adding the indirect keyword in the definition.

// does not compile with Xcode 7 Beta 2
enum Exp {
    case Number(Int)
    indirect case Plus(Exp, Exp)
    indirect case Minus(Exp, Exp)
}

Though Xcode 7 Beta 2 still does not seem to like these recursive references. My guess is that this feature will be made available in a newer build of Xcode 7.

More info: WWDC talk ‘What’s new in Swift’

map, filter & co

The definition of these handy functions and methods over collections was previously not super consistent.

In Swift 1.2, there was no default implementation of map for CollectionType – since it was not possible to have a default implementation in a protocol and extensions where only possible over classes. Partly because of that, map was defined as a method in the class Array (which implements CollectionType), but was not defined in the class Set. In addition, a global function map was declared, accepting instances of CollectionType as its first parameter. This was giving the following mess, er, I mean situation.

// Swift 1.2

let a: [Int] = [1,2,3]

// here we use map as method of Array
let b = a.map{ $0 +1 }

// here we call the globally defined map function
map([1,2,3]) { $0 + 1 }

let set = Set([1,2,3])

// does not work, there is no map method on Set
//set.map{ $0 + 1 }

// the global map function works on a Set
map(set) { $0 +1 }

Hence, depending on the type of a collection, either the global map or the map method had to be used. Not consistent and not very readable when chaining map, filter or reduce methods and functions.

Now in Swift 2, protocol extensions are allowed, map, filter & co are implemented at the protocol level, for CollectionType, as a protocol extension. Hence the same map or filter methods operate over Array, Set or any other CollectionType.

// Swift 2

let a: [Int] = [1,2,3]
let b = a.map{ $0 + 1 }

let set = Set([1,2,3])
let anotherSet = set.map{ $0 + 1 }

let sum = (1...100)
    .filter { $0 % 2 != 0 }
    .map    { $0 * 2 }
    .reduce(0) { $0 + $1 }

print(sum)
// prints out 5000

We see also in this example that filter now works on a Range. That was previously not the case since a range conforms to CollectionType but had previously no filter method implemented. Overall we have now a much cleaner syntax for these collection-related methods.

More info: WWDC talk ‘What’s new in Swift’

This is the end of this first post (1 of 3) about Swift 2. Make sure you check the upcoming article where we will be talking about exciting aspects of Swift 2 like protocol extensions and Objective-C generics, among others.

 

References

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 »