Swift 1.2

Swift is still changing rapidly, but fear not; the majority of the changes we're seeing out of Cupertino today are refinements and enhancements, not stop-gap solutions or bug fixes. Apple released Swift 1.2 as part of the Xcode 6.3 beta on February 9th, 2014, and introduced some long-awaited improvements to the syntax of the language, as well as some new features you're gonna love.

Check out the full post on the Apple Swift blog here, but for the remainder of this post, I'll take a look at the highlights and show a few practical examples of the new features and changes in Swift 1.2.

Compiler Improvements | Incremental builds

Source files that haven't changed will no longer be re-compiled by default, which will significantly improve build times for most common cases. Larger structural changes to your code may still require multiple files to be rebuilt.

Why it matters

With Objective-C projects, every time you Build & Run, the compiler goes to work on any source code files which have changed since your last build. This doesn't have much of an impact on a small project with a few source code files, and it doesn't save you any time if you're cramming your entire app into one massive ViewController, but if you have a large app, or an app with lots of 3rd party libraries, it saves a ton of time since you only have to re-compile the code that has changed.

Since Swift was first released, this wasn't the case with Swift projects, or with Swift code in your ObjC projects. The logical explanation for this is that the lack of incremental build support was a trade-off for getting rid of .h files and import statements, but the features were still sorely missed by developers working on large and medium sized code bases.

What it means for developers

With the release of Swift 1.2, we now have incremental build support for Swift modules and projects. Any source code files you change from build to build will need to be re-compiled of course, but any libraries or source code that you haven't touched since the last build won't be re-compiled, saving you a lot of time if you're the type of developer who likes to Build & Run after every couple lines of code (and c'mon, you know that's what you do).

Compiler Improvements | Faster executables

Debug builds produce binaries that run considerably faster, and new optimizations deliver even better Release build performance.

Why it matters

Going back to the '14 WWDC keynote, one of the biggest selling points for Swift was it's supposed dramatic performance advantages over other languages (specifically over languages considered to be very fast like Objective-C and Python). The developer community quickly began posting their own results and discovering that the performance improvements over Objective-C were few and far between, rather than an across the board improvement. Some tasks were even found to be slower in Swift, depending on how the code was optimized. Here's an example from Jesse Squires' blog showing Swift under-performing.

T = 10
N = 10,000
Std lib sort Quick sort
O(n log n)
Heap sort
O(n log n)
Insertion sort
O(n2)
Selection sort
O(n2)
Objective-C -O0 0.015161 s 0.011438 s 0.023984 s 1.917997 s 3.685714 s
Swift -Onone 1.300011 s 1.364851 s 3.974969 s 524.086557 s 400.251450 s
Difference -85.7x -119.3x -165.7x -273.2x -108.6x

Note that this benchmark isn't optimized for Swift, and if the -O unchecked flag is passed, as in the next table, Swift does end up out-performing Objective-C.

T = 10
N = 10,000
Release
Std lib sort Quick sort
O(n log n)
Heap sort
O(n log n)
Insertion sort
O(n2)
Selection sort
O(n2)
Objective-C -Ofast 0.012596 s 0.010147 s 0.019617 s 1.763124 s 3.504833 s
Swift -Ounchecked 0.000773 s 0.001011 s 0.002073 s 0.261637 s 0.099996 s
Difference 16.3x 10.0x 9.5x 6.7x 35.0x

What it means for developers

If you're not comfortable setting custom flags at the compiler level, you're not alone. It would be a stretch for Apple to claim Swift is superior in speed and performance, if the only way to do so was with compiler trickery, so the speed improvements shipping with Swift 1.2 unlock the potential of Swift's speed, without the need to get under the hood and tweak the compiler.

Compiler Improvements | Better diagnostics

> Clearer error and warning messages, along with new Fix-its, make it easier to write proper Swift 1.2 code. Stability improvements — The most common compiler crashes have been fixed. You should also see fewer SourceKit warnings within the Xcode editor.

This one is pretty vague, but also self-explanitory. Expect to see fewer SourceKit Crashed errors, better "Fix-It" suggestions in Xcode, and better overall stability when writing Swift code. This also bodes well for anyone using Playgrounds, as they've been a common source of instability up to this point with Xcode 6.

New Language Features

There's quite a bit to cover here, so I'm gonna hold off and do a part 2 next week to explore the newly added language features. If, however, you're champing at the bit to see what these new features do and how they'll impact the way you write Swift code going forward, here's a taste of one of my favorite new features added in 1.2:

New Features | More Powerful if let

The if let construct can now unwrap multiple optionals at once, as well as include intervening boolean conditions. This lets you express conditional control flow without unnecessary nesting.

Why it matters

Previously, if let syntax has been used as a great way to ensure a constant has a non-nil value before proceeding. That core functionality is how we unwrap optionals in Swift, and is helpful and convenient in a variety of situations (e.g. the JSON parsing post from last week). None of the existing functionality of the let keyword is going away, instead it's getting a big upgrade with some new syntax and features that will clean up alot of your code and make nested unwrapping (as with JSON parsing) a breeze.

Take a look at the code example from last week's post:

var endpoint = NSURL(string: "https://api.github.com/search/repositories?q=\(searchTerm)")
var data = NSData(contentsOfURL: endpoint!)
if let json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary {
    if let items = json["items"] as? NSArray {
        for item in items {
            // construct your model objects here
        }
    }
}

Nothing wrong with the above code, but notice how we end up with a nested structure with each additional if let? For this sample, it's not too bad, but if you have to walk down the tree 5 levels, or even 10 levels, your code spills off the screen and becomes incredibly difficult for you (let alone another developer) to read.

Here's the same code sample, updated with the new if let syntax:

if let
    jsonURL = NSURL(string: "https://api.github.com/search/repositories?q=json+parsing+swift+language:swift"),
    data = NSData(contentsOfURL: jsonURL),
    json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? NSDictionary,
    items = json["items"] as? [NSDictionary]
{
    for item in items {
        // construct your model objects here
    }
}

What it means for developers

Not only is our code now much easier to read, it's also safer. I had been constructing the data and json variables using the forced unwrap symbol ! to keep the code tidy and readable. Although it's extremely unlikely that constructing an NSURL from a string would fail, there's a small chance that the JSONObjectWithData method could fail, if the data object was in fact nil. The new syntax doesn't prevent new crashes per say, but it does a much better job, IMHO, of drawing your attention to those potentially problematic lines of code.

Notice how the new syntax allows you to write code as if everything will work properly, but still retains the safety net of aborting the current operation when one of your if lets fails.

Stay Tuned

If you enjoyed this post, be sure to check back next week for Part 2 of Swift 1.2 Explained where I'll dive in to the new language features, discuss why they matter, and show code samples for how to take advantage of the new syntax and features, including:

  • as! for failable casts
  • Nullability may now be expressed in Objective-C headers
  • Swift enums can now be exported to Objective-C using the @objc attribute
  • let constants are now more powerful and consistent
  • New native Set data structure