Monday, September 28, 2015

Swift: Type Casting

This tips and tricks explains how to perform upcasting and downcasting in Swift, using the as, as!, and as? keywords.

Keywords

Swift 2.0, type casting, as, as!, as?

Solution

Upcasting using as

Upcasting happens when you are trying to cast from a derived class to a base class. This will always succeed. The following is an example using the as keyword to perform upcasting:

        let btn = UIButton()
        let view = btn as UIView

The as keyword can also be used for literal casting:

        let strNum1 = 2.4 as Float
        let strNum2 = 2.4 as Int

Downcasting using as?

Downcasting happens when you are trying to cast from a base class to a derived class. This may not always succeed.

Consider the following code snippet:

    @IBAction func btnClicked(sender: AnyObject) {
    }

In this action for a Button view, you have an argument named sender of type AnyObject. If you want to cast this to a UIButton, you may attempt to use the as keyword:

    @IBAction func btnClicked(sender: AnyObject) {
        let btn = sender as UIButton
    }

However, the compiler will flag this as an error as the type casting may fail. Instead, you have to use as? keyword to perform a downcast, like this:

    @IBAction func btnClicked(sender: AnyObject) {
        //---downcasting---
        let btn = sender as? UIButton
        print(btn?.titleLabel?.text)
    }

The as? keyword tries to perform a downcast, and returns a value of the specified type if it succeeds, or nil if it fails. Hence, the result of the above casting is a UIButton? (optional). To use the btn constant, you need to unwrap it using the ? ( or !) operator.

A safer way to deal with downcasting is to wrap it using the if let statement, like this:

    @IBAction func btnClicked(sender: AnyObject) {
        //---downcasting---
        if let btn = sender as? UIButton {
           //---casting succeeds---  
           print(btn.titleLabel?.text)        
        } else {
           //---casting failed---           
        }
    }

Notice that using the if let statement you now don’t have to unwrap the btn constant as it is now a UIButton and not UIButton?.

Downcasting using as!

If you are very sure that the casting will always succeed, you can use the as! keyword, like this:

    @IBAction func btnClicked(sender: AnyObject) {
        let btn = sender as! UIButton
        print(btn.titleLabel?.text)       
     }

The result of the casting is a value of type UIButton. However, if you are not careful, and the casting fails, it will crash your app:

    @IBAction func btnClicked(sender: AnyObject) {
        //---casting will fail and your app will crash!---
        let tableView = sender as! UITableView       
     }

When using the as! keyword, you can also specify the destination type to be an optional, consider the following:

        var dict = [Int:Any]()
        dict[1] = "Gold"
        dict[2] = "Silver"
        dict[3] = "Bronze"
       
        print(dict[1] as! String)  //---Gold---
        print(dict[1] as! String?) //---Optional("Gold")---


The last statement converts the specified item in the dictionary to a String? (optional).

No comments: