Tech —

They grow up fast: Apple quietly bulks up Swift and Xcode in year two

From error handling to app thinning, Apple continues to nudge devs away from ObjC.

Lost in the excitement at <a href=WWDC 2015, Swift learned a few new tricks."/>
Lost in the excitement at WWDC 2015, Swift learned a few new tricks.

While most people tune in to Apple's WWDC keynote to figure out what's coming in the next version of the company's operating systems, the event is a developer's conference. Apple genuinely uses WWDC to introduce a lot of new technologies that end users will never experience directly. So with the exception of big news like Swift, the company generally does this in later, non-public talks and through the software released via its Developer Connection.

This year was no exception. While some things, like app thinning, found their way into the keynote, most technical details were buried for later. Information on the new version of Xcode was scattered throughout the week's panels, and developers were able to get a copy of a beta with plenty of preliminary documentation. In the time following WWDC, we spent a couple of weeks watching conference sessions and looking through both the software and all this documentation. The research gave us a sense of some of the under-the-hood changes that are coming for developers this fall alongside the shiny, new operating systems.

Again, much of this won't reach an end user directly, so this isn't meant to be an exhaustive account. However, Apple has introduced several new tweaks that stood out to us upon further reflection.

A tale of two languages

When we first looked at Swift last summer, we predicted it was Apple's future. Objective-C wouldn't go away any time soon, but Apple would almost certainly nudge developers toward the company's new baby for a few years before turning the nudge into a violent shove.

Such nudging has begun. For years, Apple has been adding new features and syntax to the Objective-C language, things like automatic reference counting and closures. These features have generally made it easier and safer to develop in a language that can easily let you shoot yourself in the foot or make ObjC a better fit for some of the design patterns of Apple's own frameworks.

This time around, ObjC gets a grand total of two new features. One of these is a useful feature stolen from Swift (generics); the second lets ObjC behave a bit closer to Swift's expectations (nullability). Realistically, the only reason either of them are here is to make it a bit easier for projects to mix code from the two languages. (Although ObjC developers did get a new tool to help diagnose memory-related crashes—see below—it's not a language feature.)

Swift, on the other hand... Swift gets bumped to version 2.0. This language has received a lot of attention. But let's be clear: a lot of that attention was needed to bring the new language closer to where ObjC was already. That doesn't mean that the new features aren't good; it's just that with one major exception, they're playing catch up.

Error handling

One of these new additions is error handling. Apple had added it to ObjC a number of years ago now, but the feature was glaringly absent from the first version of Swift. Error handling is useful for situations where something can go wrong that's beyond your control. A great example is reading data from a file; the file may not exist, the disk it's on could get ejected, etc.

There are two ways of handling this. You can either exhaustively check for every error you can think of (running the risk that you won't think of everything), or you could just try it and see if anything goes wrong. Error handling represents the latter approach.

If any of your methods can conceivably create an error, you note this by adding "throws" to the method definition. Within that method, when an error is encountered, you simply use the "throw" keyword to signal the error and exit the method. Throw has to be followed by a member of an enumerator that implements the ErrorType protocol. Any calls to the method have to be prefaced with the "try" key word.

Structurally, none of this is exceptionally unusual. It lacks the more elaborate exception catching of many other languages, which can handle basic problems like division by zero or access to bad memory addresses. But it's still pretty useful, especially given that Apple is making sure its own methods (which are still mostly written in ObjC) throw errors that can be caught by Swift.

The weirdness comes in the syntax. In most languages that use it as a keyword (including Swift 1.0!), "do" is used to start loops that are closed with the "while" keyword. For looping, Apple has replaced "do" with "repeat" (which is fair enough, as far as it goes) and then repurposed "do" to start a block of code that includes statements that throw errors. This is just confusing for anyone burdened by any sort of programming history.

For those who are free of this burden, the resulting code is pretty readable.

func readMyFile( myFile: String ) throws {
 
 // I can't believe this method doesn't take an NSURL
 if !NSFileManager.defaultManager().fileExistsAtPath( myFile) {
 throw fileError.fileIsMissing
 }
 
 guard NSFileManager.defaultManager().fileExistsAtPath( myFile) else {
 throw fileError.fileIsMissing
 }
 
 // do stuff with the file, which we're assured exists
}

enum fileError: ErrorType {
 case fileIsMissing
 case fileIsBusy
 // other cases as needed
}


var fileString = "~/myfile" // tilde represents a user's home directory
fileString = fileString.stringByExpandingTildeInPath

do {
 try readMyFile( fileString )
}

catch fileError.fileIsMissing {
 // handle the error appropriately
}

catch fileError.fileIsBusy {
 // different handling here
}

Other problem solvers

Languages like Java also have a "finally" keyword that executes no matter what errors do or don't occur. For example, if your code opened a file and then threw an error, you'd want to make sure the file is closed again—the code to do so could go in a finally code block, which is located after all the error catching code.

Apple decided to do something a bit more flexible by adding a "defer" statement. Any block of code following a defer statement will always be executed as the last thing done before a method returns. This works whether the code returns normally or by throwing an error. And it doesn't matter at all where the code is put within a method; it always gets executed last.

If we were to open our file above, we could simply close it in the very next line in a defer statement. The whole rest of the method could then use the open file, throw errors, return data, etc. No matter what happens, the file gets closed at the end of it all. It makes for a nice logical grouping of code.

In addition to methods, defer statements can go inside loops (getting executed each time through) and any other blocks of code (like those following do). You can even stack multiple defers, which get executed in a last-in-first-out manner.

Another way of catching problematic situations that Apple has added is the "guard" keyword. Guard can be thought of as similar to an "if" statement that can only execute "else" blocks. Let's use the same example of checking whether our file exists. If you have a bunch of conditions you need to check in a series of if/else statements, it can lead to complicated code. And it's not always easy to see the intent of the programmer—the little ! at the start of the conditional makes all the difference, but it's easy to overlook.

Now we can use guard instead:

guard NSFileManager.defaultManager().fileExistsAtPath( myFile) else { 
// throw the exception

Rather than testing whether something isn't true, you're guarding against it being false, and the resulting code's a bit more readable.

Another new feature of Swift is rather nice but fairly technical. Both Swift and ObjC use things called protocols, which you can view as a promise: any class that adopts a protocol guarantees it will implement any methods specified in the protocol. That way, you can have a whole series of classes that all have a certain minimal behavior, but these can differ wildly in how they implement that behavior and in the other behaviors that they have.

Swift also has extensions, which are similar to ObjC's categories. These are essentially a way to add new methods to a class without subclassing it. They're especially useful for adding behavior to classes in Apple's frameworks—for example, you could give all mutable arrays a random shuffle method.

In Swift 2.0, you can now extend protocols, making it easy to add required methods (and default implementations) to existing protocols.

Channel Ars Technica