Seven Swift 2 enhancements every iOS developer will love

Apple makes good on Swift’s emphasis on performance, approachability, and ease in latest update

Seven Swift 2 enhancements every iOS developer will love
PeopleImages / Getty Images

When Apple introduced Swift in June 2014, the initial fanfare centered on the brand-new language’s emphasis on performance, approachability, and ease. Now, with Swift 2 out more than a year later, Apple has made good on that emphasis, pushing Swift closer to the kind of readability and maintainability development shops and IT organizations expect of a mature language.

Many of the enhancements to Swift, through both the Swift 2.0 update and subsequent Swift 2.1 update, have made the language more explicit and intentional, and in turns, Swift 2 code will be safer and easier to maintain for years to come (especially now that Swift is open source). New language constructs (keywords) in Swift 2 improve the readability of control flow -- the order in which lines of code are executed. Thanks to these new keywords, collaborating on Swift code will be much more productive and efficient.

Apple has also enhanced Xcode 7 with new features and modernized its SDKs across OS X 10.11, iOS 9, and watchOS 2. The API overhaul even includes new features for Objective-C, including lightweight generics and OptionSetType, which makes Objective-C APIs Swift-like when invoked from Swift code.

Easy-to-follow code is a vital concern for development teams, especially in business contexts, as “witty” or complex code reduces maintainability, introduces and hides serious bugs, and can lead to security leaks. Here are seven new features available with Swift 2 that will help you and your team collaborate more effectively on a more efficient, secure code base.

guard keyword

Swift 2’s new guard keyword provides precondition checking in a method -- exactly like you might find with an if/else statement in Objective-C.

There are two wins here:

Firstly, the guard keyword makes it clear that you, as the programmer, are asserting that certain preconditions must be met before executing the code that follows.

Secondly, the guard keyword makes the control flow easier to read. Instead of injecting a gigantic if/else structure with no clear beginning or end, all of the assertion and cleanup code is compacted into one location.

This makes early exits from Swift methods and functions easy for fellow programmers to follow and explicitly clear -- a win for code maintainability.

Code that follows a guard statement can safely assume all of the parameters required to perform an action are ready to use:

// A value type struct to store data (passed by value i.e. copied!)
struct EmailSubscriber {
   var firstName: String
   var email: String
}
// A supporting function that illustrates valid/invalid values
func validateEmail(email: String) -> Bool {
   return true// false // Implementation as a reader exercise
}
// Create a new subscriber if the parameters meet the requirements
func createEmailSubscriber(firstName: String, email: String) -> EmailSubscriber? {
   // Prevent empty text input from the user
   guard firstName.characters.count > 0 else {
       print("Invalid First Name")
       return nil
   }
   // Assert it's an accepted email format: i.e. Paul@SuperEasyApps.com
    guard validateEmail(email) else {
       print("Email Format Error: Invalid Email")
       return nil
   }
   // Any code path that reaches this point has been validated
   return EmailSubscriber(firstName: firstName, email: email)
}
// Missing data results in a nil value
let invalid = createEmailSubscriber("", email: "Paul@SuperEasyApps.com")
print("Subscriber is invalid, returned: ", invalid)
// Attempts to force unwrap and use nil values causes crashes!
// Tip: Use the if/let syntax to work safely with optional types
//print("Subscriber is invalid, returned: ", invalid!.firstName) // ERROR!
// Complete data results in a new digital subscriber value/object
let valid = createEmailSubscriber("Paul", email: "Paul@SuperEasyApps.com")
print(valid!.firstName, "'s email is: \(valid!.email)")

This enhancement reinforces one of Swift’s core strengths: It provides safety mechanisms to protect against invalid or optional values. Thanks to guard, your core app logic will be simpler to express (and maintain) because you will have blocked the invalid state in advance.

defer keyword

Swift 2’s new defer keyword adds a level of control flow that didn’t previously exist in Swift. The defer keyword allows you to easily clean up an expensive or limited resource. For example, if you are making the next Dropbox app in which you are reading and writing thousands of files, you would hit the maximum number of file descriptors for your app’s process if your code fails to close files after reading and writing completes. Using the defer statement, you can be sure that all files are closed either when the file processing completes or if an error occurs.

The key strength of defer is that it allows you to keep related code together, rather than having to split code into segments at the top and bottom of a method. Cleanup code (file close) can be written directly below the creation of an expensive or limited resource (file open) in a defer closure that will execute at the end of the current scope:

func fileProcessing() {
   print("1. Create file descriptor #1 and start file processing") 
   defer {
       print("5. Close and cleanup file descriptor #1 (I/O resource)")
   }
   print("2. Create file descriptor #2 and start file processing")
   defer {
       print("4. Close and cleanup file descriptor #2 (I/O resource)")
   }
   print("3. Finish file processing")
}
// Look at Xcode's Console output for order: 1, 2, 3, 4, 5.
fileProcessing()

In the previous code block, the messages will print to the Xcode console in the order 1, 2, 3, 4, 5. The defer keyword causes your code in the defer block to execute after the current scope’s closing curly bracket.

Multiple defer code blocks can be added inside your methods and functions. Each defer closure will execute in the reverse order it is called -- this ensures cleanup safety when combined with the new error handling model in Swift 2.

repeat/while and do scope

Apple recently made a surprise change to Swift’s control flow: The do/while loop is now the repeat/while loop.

It’s a small change, and it goes against the convention of established languages like Objective-C, JavaScript, C++, and Java. The rationale is that the repeat keyword provides an instant cue that the block of code will repeat, enhancing code readability:

// A method that counts down and outputs to Xcode’s Console
func repeatWhile() {
   var x = 10
   repeat {
       print("T-minus", x, "seconds")
       x = x - 1// x-- is being deprecated in Swift 3.0!
   } while x > 0
   print("Blast off!")
}
// Call the method in Playgrounds
repeatWhile()

The do keyword has been repurposed to create new scope, unlike every other popular language that uses it for loops. Now, when you want to create an inner scope inside an existing scope, you use do. This is important when you need to deallocate expensive resources in a tight loop or if you want to help ARC (Automatic Reference Counting) effectively clean up expensive resources:

// Outer scope
let x = 7
do {
   // Inner scope (new x variable masks outer scope x variable)
   let x = 10 
   do {
       // Inner inner scope ... inception
       let x = 200
       print("x:", x) // x: 200
   } 
   print("x:", x) // x: 10
}
// outer scope
print("x:", x) // x: 7

As with C++ or Objective-C, you can nest do scopes to enhance your local variable lifetime control.

Where is this useful? If you process 15-megapixel images or large data files sequentially in a tight loop, you can run out of memory, which will cause iOS to force quit your app. There’s a hard memory footprint limit on iOS -- once you pass it, your app “crashes” and you have an unhappy customer. Using the new do scope, you can avoid the crash and deallocate memory (using ARC) after each filter pass, instead of at the end of a tight loop.

These new and repurposed Swift keywords play perfectly with the new error handling model in Swift 2.

error handling

Swift 2’s new error handling is one of the pillars of the improved language. It provides a feature that has been missing from Swift and is expensive (performance-wise) to use in Objective-C.

Error handling in Swift creates an implicit control flow structure for dealing with unexpected issues from networking, input, and data. Swift uses the new do keyword for scoping the traditional try/catch structure popular in Java. In Swift, you use do/catch blocks.

Swift 2’s error handling isn’t a cure-all. Instead, it’s designed to help you deal with recoverable issues, such as invalid input from user forms or invalid (or unexpected) response data from a Web server. Error handling can also be used to handle file I/O issues, networking availability, and other common problems associated with poor cellphone reception and limited disk storage on mobile phones.

Error handling is built into the method signature using the throws keyword. With native language support for error propagation, the additional NSError pointer parameter is dropped from all APIs because it is redundant.

Every line of code that can fail (unsafe code) must be marked with the new try keyword. This makes it easier for everyone working on the code to quickly know that a particular line of code could fail.

In dealing with code that can fail, you have three options:

  1. Ignore the error using the try? keyword, thereby unifying the optional type system with Swift 2’s error handling model. The use of try? ensures that all errors (if thrown) are consumed and an optional value is returned as a right-hand value.
  2. Handle any errors that are thrown from unsafe code using the do/catch statements.
  3. Propagate errors by adding the throws keyword to the method or function signature. Unlike Java, errors do not propagate by default.

Error handling in Swift 2 improves on the Objective-C error model -- which relies on documentation and comments. Swift 2’s error-handling model provides safety through compile-time checks that prevent unsafe code from entering your code base.

For example, you can leverage error handling in code that validates a customer has typed in an authentic name and email address:

// Create a custom error type by conforming to the ErrorType protocol
enum ValidationError: ErrorType {
   case InvalidName
   case InvalidEmail
   case InvalidAge(age: Int)
}
// Check name against your name policies
func validateName(name: String) throws {
   // Use guard statements to prevent invalid user input
   guard name.characters.count > 0 else {
       throwValidationError.InvalidName
   }
}
// Process a new customer using required attributes
func onboardNewCustomer(name: String, email: String, age: Int) {
   do {
       print("Started onboarding")
       // You must use the try keyword for any method that can throw an error
       try validateName(name)     
       // Exercise: Validate other required attributes (age, email, etc.)     
       // Finished processing if no errors
       print("Finished onboarding")
   } catch let error as ValidationError {
       // Using a local variable you can catch all ValidationErrors     
       // The local error variable can be handled with a switch
       switch(error) {
       case ValidationError.InvalidName:
           print("Invalid name!")
       case ValidationError.InvalidEmail:
           print("Invalid birthday!")
       case ValidationError.InvalidAge(let age):
           print("Invalid age \(age)!")
       }
   }
   catch {   // default error catch
       print("Catch errors here:", error) // error is default name
   }
}
onboardNewCustomer("", email: "", age: 12223) // Invalid!
onboardNewCustomer("paul", email: "Paul@SuperEasyApps.com", age: 29)

The new error handling model works well with the new defer keyword in recovering and running additional lines of code if there is an error thrown in your codebase. This is similar to Java’s finally keyword.

protocol extensions

Apple’s v1.0 release of Swift came with some quirks, like the plethora of global free-standing functions that were poorly documented and hard to find within the Swift Standard Library. Developers who rely on code completion to code faster (myself included) had trouble figuring out how to write code after years of experience with Objective-C.

Protocol extensions allow any protocol (for example, interface in Java) to be enhanced with both new behavior and state. This is huge for Swift and it’s going to change the face of the Swift Standard Library, open source code, and how you approach developing your code base.

The introduction of protocol extensions also brings new APIs, and with this, most free-standing global functions have been removed. As a result, the standard dot syntax variableName.methodName() is now readily available for you to use, thereby enhancing API discoverability and coder productivity.

With protocol extensions in Swift 2, if a feature or method doesn’t exist, you can write your own. For example, you can extend the Array structure that Apple provides with new functions or methods. In Objective-C, the NSMutableArray has a method to remove multiple elements as a single line of code, named removeObjectsInArray:. In Swift (2.1) there is no method to remove multiple elements from the Swift Array structure using a single method call. Using a protocol extension, you can add this method to the Array structure.

// Extend the Array class when Elements are comparable
extensionArraywhere Element: Equatable { 
1 2 Page 1
Page 1 of 2