Seven Swift 2 enhancements every iOS developer will love

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

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. PaulSolt@iPhoneDev.tv
    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: "PaulSolt@iPhoneDev.tv")
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: "PaulSolt@iPhoneDev.tv")
print(valid!.firstName, "'s email is: \(valid!.email)")
Download Swift Playground files

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()
Download Swift Playground files

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()
Download Swift Playground files

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
Download Swift Playground files

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: "PaulSolt@iPhoneDev.tv", age: 29)
Download Swift Playground files

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 { 
   // Remove a single Element if it is found in the Array
   mutating func removeObject(object: Element) {
       if let index = self.indexOf(object) {
           self.removeAtIndex(index)
       }
   } 
   // Remove multiple Elements using the previous method and a loop
   mutating func removeObjectsInArray(array: [Element]) {
       for object in array {
           self.removeObject(object)
       }
   }
}
var students = ["John", "Sue", "Michael", "Chris", "David", "Benjamin"]
var studentsToRemove = ["Michael", "David", "Benjamin"]
print("Students: ", students)
// Remove an array of student names
students.removeObjectsInArray(studentsToRemove)
print("Students: ", students)
students.removeObject("John")
print("Students: ", students)
Download Swift Playground files

This kind of protocol extension will allow you to reuse a few lines of code in your app without having to copy/paste the same logic into every code file you need to maintain.

OptionSetType

Apple has worked to make Swift more approachable, and one such change is Swift 2’s movement away from traditional bit mask options. Swift 2’s new OptionSetType uses set logic instead of bitwise operations (which is not the best-understood topic for beginners, nor should it be required).

Traditional Objective-C and C APIs have used bitwise operations to logically combine multiple options in order to pass as a single value -- an optimization at the bit level that requires an understanding of how bit masks and bitwise operations work. With OptionSetType it’s no longer necessary to fully understand bit masks to create and sell apps on the App Store.

This is a win for developers because it frees them of a legacy programming model that requires more hard-to-read boilerplate code.

Apple has also revamped all of the Objective-C APIs that required NS_OPTIONS values to use the new OptionSetType. This causes breaking code changes, but continues to move Swift’s syntax forward along with the APIs that it supports.

The OptionSetType makes it easy to pass multiple settings as a single parameter, without requiring you to become an expert in binary arithmetic:

// You can create custom flags or options using OptionSetType
struct VideoFormat: OptionSetType {
   let rawValue: Int
   // Use static let values within the struct and assign
   // the raw value using bitmask values (1, 2, 4, 8, 16, etc)
   static let Video1080p = VideoFormat(rawValue: 1)
   static let Video720p = VideoFormat(rawValue: 2)
   static let EnableStreaming = VideoFormat(rawValue: 4)
   static let EnableDownloads = VideoFormat(rawValue: 8)
}
// Use Swift Set notation to pass options
func startVideoPlaybackWithOptions(videoStreamOptions: [VideoFormat]) {
   print("Play video stream:", videoStreamOptions)
}
// Store a collection of options using Set notation
let videoOptions = [VideoFormat.Video1080p, VideoFormat.EnableDownloads]
startVideoPlaybackWithOptions(videoOptions)
// Set notation makes legacy bitmask operations much easier to read
if videoOptions.contains(VideoFormat.Video1080p) {
   print("Video is 1080p!")
}
Download Swift Playground files

Modernized SDKs and Objective-C enhancements

The last time I wrote about Swift, I wasn’t sure if Apple would continue to support Objective-C with new features. With Swift 2, Apple has proven its commitment to Objective-C.

Enhancements to Objective-C have been made to directly support writing code in Swift that calls Objective-C APIs (most of Apple’s Cocoa Touch and Cocoa SDKs).

Apple added lightweight generics to Objective-C that allow collection types to store extra type information about the elements. This provides more type safety in Objective-C, but it also means that any API using a collection type passes on the same type safety when accessed from Swift code.

The touchesBegan(_:withEvent:) method from the UIResponder protocol in Swift now supports typed collections and has been remastered for Swift 2. Take a look at the Swift syntax and API evolution from Swift 1.1 to Swift 1.2 to Swift 2.1 as Apple released fallible initializers, the Swift Set collection type, and added Objective-C’s lightweight generics:

class ViewController : UIViewController {
   // Swift 1.1 (APIs used Objective-C NSSet type)
   override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
       if let touch = touches.anyObject() asUITouch? {
           let location = touch.locationInView(self.view)
           println("Touch: \(location)")
       }
   }
   // Swift 1.2 (Swift Set introduced along with new as? keyword)
   override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
       if let touch = touches.firstas? UITouch {
           let location = touch.locationInView(self.view)
           println("Touch: \(location)")
       }
   }
   // Swift 2.1 (Objective-C gains lightweight generics + Swift API update)
   override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
       if let touch = touches.first {
           let location = touch.locationInView(self.view)
           print("Touch: \(location)") // New print() method
       }
   }
}
Download Swift Playground files

The strong type system in Swift provides enhanced type safety with compile-time checks to prevent errors before they can become runtime crashes (it is better to fix a bug early), and that’s why compile-time error checking is so important.

Apple’s other enhancements include API availability checks for compile time assertions that ensure all source code being used is supported on the current iOS version selected in Xcode. Availability is important for any company that needs to release a public SDK for app developers to integrate into their own apps. Lastly, nullability helps the Objective-C APIs work with Swift’s optional type system and makes the imported Objective-C APIs Swift-like using optional typing information.

Conclusion

Apple’s introduction of Swift 2 does break existing code, yet the latest version 2.1 (as of this writing) is a major leap forward as Swift continues to mature. These breaking code changes are vital to Swift if it is going to become an industry standard in the development world.

Swift was open-sourced in December 2015, when Apple published a road map of upcoming features for Swift 2.2. Apple has also opened the discussion to the development community for the direction of Swift 3.0 and beyond.

In Swift 2, new control flow keywords and language structures allow you to make your intentions clearer using the first line of code you write. This straightforward approach allows you to know what is happening in a block of code and improves your ability to properly maintain your app’s control flow. The guard keyword enables early exits and protects code logic from having invalid inputs. The defer keyword allows you to clean up resources when they finish or in the event of an error.

The renamed repeat/while loop and the do scoping continue to provide support for both readability and Apple’s new error handling model, which is driven by programmer intent. Any method declaration (method signature) that is potentially unsafe is marked with the new throws keyword. Any method call to an unsafe method (one that can throw errors) is marked with the new try keyword. Error handling in Swift is performant, on par with return statements, unlike Objective-C -- which is one more reason to adopt error handling using Swift for your next app project.

Along with the changes to control flow, Apple has modernized existing APIs for the OS X v10.11, iOS 9, and watchOS 2 SDKs using new language features in Objective-C that enable you to use Objective-C APIs in Swift seamlessly.

What this means for you is that the all-new Swift 2 is ready to use, and everything about it feels like Swift, even if you’re leveraging Apple’s vast Objective-C APIs and extensive SDKs. If you’re not already on board, the time is ripe to start your next app in Swift.

Related articles

Join the Computerworld newsletter!

Error: Please check your email address.

Tags Swift

More about AppleARCDropbox

Show Comments

Market Place