The big news in the Swift community is the release of Swift 1.2, featuring some awesome features. Apple wrote about that a bit here, this was before they released Xcode 6.3 Beta 2, which came out today. But it contains most of the major language changes.
Xcode 6.3 Beta 2
First of all, Xcode itself has changed to allow for more easy to use playgrounds. In particular Apple has added rich text comments, in addition to showing results inline rather than in a separate timeline view. Swift performance was also further improve, and an additional method called zip was introduced for merging Swift sequences together.
zip()
Zip is a method that comes straight out of Haskell which accepts multiple sequences of data, and returns a single tuple “zipping” the two data sets together. For example
let a = [1,2,3] let b = ["a","b","c"] let zipped = zip(a, b)
Here the value of zipped would become two sequences in a tuple with types [Int] and [String]. Casting this to an Array makes the significance of this a little more clear:
println(Array(zipped))
[(1, a), (2, b), (3, c)].
As you can see the two sequences are simply “zipped” together. This does beg the question though, what if the sequences are of differing lengths?
For example…
let a = [1,2,3] let b = ["a","b"] let zipped = zip(a,b)
In this case, the zipping process will use the shortest sequence’s length as the number of elements to zip, and simply discard the trailing elements on the longer sequence. Giving the output, [(1, a), (2, b)]
This is particularly useful when working with what are effectively infinite sequences. You can limit the list by zipping with a finite sequence:
let squares = Array(map(1..<10) { $0 * $0 }) let names = ["one", "two", "three"] let squareList = zip(names, squares) println(Array(squareList))
Results in:
[(one, 1), (two, 4), (three, 9)]
So, even though the squares sequence can go on forever, it's made in to a finite list due to the fact that names has only 3 elements.
Goodbye pyramid of doom!
The optional binding sometimes leads to messy code like this:
if let first = user.first { if let last = user.last { return "\(first) \(last)" } }
But with 1.2, these series of if let statements can be collapsed down to just a comma separated list of optional values, and only a single set of curly braces. The above code will behave exactly the same as this:
if let first = user.first, last = user.last { return "\(first) \(last)" }
Sometimes these chains upon chains of optional binding statements can get out of hand, so this is a very welcome feature. As an additional bonus in Xcode 6.3 Beta 2, we can now also add a single optional binding let clause. For example:
if age > 17 && profile.public, let first = user.first, last = user.last { return "\(first) \(last)" }
This code will not only perform the optional binding in a more concise way with the variables first and last, but it will only do so if the user is of age and has a public profile available, which are assumed to be boolean values in this example. Contrast that with how we wrote this in older versions:
if (age > 17 && profile.public) { if let first = user.first { if let last = user.last { return "\(first) \(last)" } } }
The as! keyword was added
When casting from one type to the other, it's possible that the cast could fail. Previously this was handled with the case of an optional using the as? operator. Consider the scenario below
func bio(name: String, location: String) -> String { ... } var name: AnyObject name = "Jameson" var location = "Austin, TX" let nameStr = name as String bio(nameStr, location)
Here we are calling a function that requires a String, but we have name typed as an AnyObject object. Before Swift 1.2 this code would be accepted, but this could actually crash your application if the cast fails. In this case it would never fail, so we can safely do this cast, but the syntax shown above does not clearly demonstrate that this cast is actually dangerous. If name gets changed to something that is not castable to a String later, this code would cause the app to crash. In order to more clearly communicate this, the as operator must now become as! if the cast could possibly fail. So the above would now become this:
func bio(name: String, location: String) -> String { ... } var name: AnyObject name = "Jameson" var location = "Austin, TX" let nameStr = name as! String bio(nameStr, location)
This helps drive home an important message: If you are using an exclamation mark (!), then you may be setting up a situation where the app could crash due to a failed cast, or null value. As a general rule, you should just never use forced casts unless you absolutely have to (or if you're writing code that won't go in to production)
Keep in mind the as? keyword is unchanged, and in general you should prefer using it in addition to an optional binding for this type of scenario, as this is much safer, e.g.
func bio(name: String, location: String) -> String { ... } var name: AnyObject name = "Jameson" var location = "Austin, TX" if let nameStr = name as? String { bio(nameStr, location) }
Xcode and Compiler Improvements
This is probably the biggest one for me. The compiler is faster due to incremental builds, and Xcode doesn't crash all the time due to some stability improvements. This alone makes the beta work the download and I highly recommend getting a copy on iOS Dev Center.
Want to learn more about Swift 1.2? Try out some of my free tutorials. My Core Data tutorial was updated recently for 1.2, which I would recommend to anyone interested in working on real iOS Apps.