Hands-On Swift 2 [Part 1]

Swift 2 is a major release of the Apple’s Swift programming language that comes with a bunch of new capabilities. This tutorial goal is to get you up and running with those new tools and arms, by doing quick and straightforward examples.

Some of the great features you will get practice on this part of tutorial, are: Error Handling, Availability Checking and Defer Actions!

Without further ado, let’s dive right in!

Open up Xcode 7, then select File\New\Project from the menu, choose the Single View Application template and name the project “Swift2HandsOn”.

Availability Checking:

The compiler can now check whether the API you use is available for the deployment target of your project. For example, if you are targeting iOS 9 and you use a method that is last defined in iOS 8, then Swift will throw a compile error. This way, you can avoid runtime potential crash and inconvenience.

Let’s try this out, select the ViewController.swift file from the Project navigator view, and place the following code inside the viewDidLoad() method:

let stackV = UIStackView(frame: CGRectMake(0, 0, self.view.frame.width, self.view.frame.height))
self.view.addSubview(stackV)

The following code will instantiate a UIStackView object and add it to the view. If you build the project, it will pass the compile process without issues, this is because you are currently targeting the latest iOS SDK version (iOS 9) in which the UIStackView class is available. Let’s try to decrease the deployment target of your project. Select the project name from the Project navigator view, then click on the General tab, from the Deployment Info section, select iOS 8.4 from the drop down list.

Change the Deployment Target in Xcode

Now move back to the code you just added, you will instantly get a compile error highlighted in red.

Checking API Availability in Xcode 7

The error says it all, you are trying to use UIStackView in an older SDK version while it is only available in iOS 9 and later.

So, how can we avoid this and pass the compiling process smoothly ?

Remove the code you added previously and place the following code instead:

if #available(iOS 9, *){
     // UIStackView is available
     let stackV = UIStackView(frame: CGRectMake(0, 0, self.view.frame.width, self.view.frame.height))
     self.view.addSubview(stackV)
}else{
     // UIStackView is not available, use UITableView or UICollectionView instead
     let tableV = UITableView(frame: CGRectMake(0, 0, self.view.frame.width, self.view.frame.height))
     self.view.addSubview(tableV)
}

The code above uses an availability condition with the #available directive, the block of the if will always executes when your device (or the simulator) is running iOS 9 or newer. Otherwise, the else block will run instead.

So in your case, if you build and run the project on an iOS 8.4 device (or older), you will notice a table view object is shown instead of the stack view. But if you run on an iOS 9 device, a stack view object will be added to the view.

The #available directive accepts few parameters, in general, here is its form:

if #available(platform name version, ..., *)

It first accepts all platform names and versions (separated by a comma). Of course, you can specify just one platform if you want. The last argument represented by an asterisk is required, and indicates that the if block executes on the minimum deployment target of the project.

Here is another format for the availability condition:

if #available(iOS 9, OSX 10.10, *) {
    // This block will run only for iOS 9+ and OSX 10.10+
}else{
    // If the iOS target version is lower than 9 or if the OSX target version is lower than 10.10, then this block will run instead
}

Error Handling:

A program can throw errors during its execution, an error common example in games is when the user try to acquire a consumable good like weapons, extra lives, etc, while his coins credit is insufficient to fund that good value.

Swift 2 is introducing a new way to respond to errors and react accordingly without breaking the program execution. In this section, I will show you how should error handling be done through a practical example.

Create a new Playground file (File\New\Playground) from the menu and name it “GamePlayground”.

In the following example, we are going to simulate a game store where purchase operations are done. Two kind of errors can be thrown in a store operation: Either when the requested item is not available in the store, or when the user credit is not enough to purchase the item (of course there could be other errors, but let’s keep it simple for now).

Remove everything you see in the playground file, and start by defining the error types for your game store. Place the following code to do so:

enum GameErrors:ErrorType{
    case InsufficientCredit
    case WeaponOrGladiatorNotFound
}

Swift enumerations is the suitable way to represent a group of related error conditions, all errors in Swift should be of type conforming to the ErrorType protocol.

In this case, you defined two error conditions for your game store. The first will be raised when the user credit is insufficient to purchase the item, and the second error condition will be fulfilled when the requested item is not found in the store.

Copy the following class implementation below the enum data set you defined previously:

class StoreManager{
    //1
    struct Item {
        var price : Float
        var count: Int
    }
    //2
    var store = [
        "Knife"   : Item(price: 200, count: 7),
        "Bazooka" : Item(price: 1100, count: 20),
        "Missile" : Item(price: 3000, count: 10),
        "BasicGladiator"  : Item(price: 20000, count: 1),
        "SilverGladiator" : Item(price: 50000, count: 1),
        "GoldGladiator"   : Item(price: 120000, count: 1),
        "MagicGladiator"  : Item(price: 350000, count: 1)
    ]
    //3
    func purchaseItem(userCredit : Float, itemName name : String) throws {
        let item = store[name]
        if item == nil{
            throw GameErrors.WeaponOrGladiatorNotFound
        }else if userCredit < item?.price{
            throw GameErrors.InsufficientCredit
        }else{
            print("You can now acquire the requested item")
        }
    }
}

The code above worth a brief explanation:

//1 : This is a simple structure to represent an item type in the store. An item is characterised by a price and its quantity available in the store.

//2 : All items in the store are regrouped in an array called store. Each element in the array is of type Item, the struct type you defined previously.

//3 : This method tries to pass a purchase operation from the store. It accepts two parameters, the first indicates how much is the user credit, and the second represents the requested item to purchase from the store. Note the keyword throws in the method declaration, that is to indicate that this method can throw an error during its execution, and hence, the method is now called a throwing method.

The code in the method body tries to verify whether the item is available among the store elements and whether the user credit is enough to acquire such item. Should one of the conditions is not fulfilled, an error is thrown (either WeaponOrGladiatorNotFound or InsufficientCredit). If all conditions are fulfilled, the method continues execution to the last else block.

The method throws errors, ok, now what ?

Well, now you should handle that error and respond accordingly. If the requested item is not available in the store, then you may need to tell the user about that. If his coins credit is not enough to purchase an item, you will ask him to purchase more coins with real money to recharge his credit.

How to handle errors in Swift 2 ?

do..try..catch

In Swift 2, you respond to errors by using a do-catch statement. Let's dive right in the example to understand how it works. Copy the following code after the closing bracket of the class:

var storeManager = StoreManager()

do
{
    try storeManager.purchaseItem(200000, itemName: "BasicGladiator")
    // ...
    print("All code after a try statement runs only if no error was thrown")

}catch GameErrors.InsufficientCredit{
    
    print("Your don't have enough credit to acquire this item")
}catch GameErrors.WeaponOrGladiatorNotFound{

    print("The weapon you are trying to acquire is not available in the store")
} 

The code above will instantiate the class StoreManager, then it will call the purchaseItem method. Note the try keyword before the method call, remember that the purchaseItem method is a throwing method (declared with the throws keyword), hence you should precede its call with the try keyword.

The two catch blocks will handle the errors you defined in the enum data set. Note the name of the catch clause that corresponds exactly to the name of the enum errors ( catch GameErrors.InsufficientCredit).

Note: The execution of all the code you put after the try statement is not guaranteed, unless no error was thrown by the program.

So far, the playground will instantly execute the class code, you should see the results of the execution in the Results sidebar in the right of the editor.

For example, here is the results of your program execution so far.

Playground results sidebar

No error was thrown, the item purchase operation from the store seems to pass all the tests and hence the code after the try statement is executed 🙂

Try to blew it up by changing the item name in the method call to something that doesn't figure in the store items, something like this:

try storeManager.purchaseItem(200000, itemName: "ItemNotFound")

From the Results sidebar in the right, you should notice the GameErrors.WeaponOrGladiatorNotFound error is thrown and the block of code inside its catch clause is executed. Try again, keep a valid name for the item, but this time change only the first parameter to a very small value and check how the results output will change in the results sidebar by throwing the InsufficientCredit error.

Clean up with the defer statement

The try statement will result in the code execution to leave the current block of code, and it will not probably get back where it left especially when the throwing method executed from the try statement effectively throw an error.

Let's go back to the previous example:

try storeManager.purchaseItem(200000, itemName: "ItemNotFound")
// ...
print("All code after a try statement runs only if no error was thrown")

The print statement will not run if one of the errors in the purchaseItem method is thrown, but, what if you need to run some code after the try statement anyway, regardless of whether an error is thrown? In such case, the defer statement come to rescue. Change the do-catch statement code to the following:

do
{
    defer{
        print("I will output this regardless of whether an error is thrown")
    }

    try storeManager.purchaseItem(200000, itemName: "Bazooka")
    // ...
    print("All code after a try statement runs only if no error was thrown")
    
}catch GameErrors.InsufficientCredit{
    
    print("Your don't have enough credit to acquire this item")
}catch GameErrors.WeaponOrGladiatorNotFound{
    print("The weapon you are trying to acquire is not available in the store")
    
}

The only code you added above is the defer statement. As the name indicates, the defer statement defers the execution of its block until the execution of the current scope is finished. The most important thing to note here is that the defer statement will run regardless of the whether an error occurred.

In the example above, you simply placed an output call. However, in your projects, you may need to call some methods to do some cleanup tasks (reinitialise the game scene, freeing memory, etc).

Where to go from here?

This is the first part of all the Swift 2 arsenal, you can read the second part here 🙂

Leave me a comment below if you have any question or suggestions, I'd love to hear your thoughts!

Malek
iOS developer with over than 11 years of extensive experience working on several projects with different sized startups and corporates.