Swift 3.0 - What's is, as, as? and as! operators?

We all have been there where language offers plethora of options. However, I have quite experienced that bunch of features sometimes cause confusion even though individual feature is easier to understand. When all the features come at once, it gets quite scary to remember what goes where.

In this post I will try to demystify the little concepts involving keywords which are not too long, but can cause bit of a confusion. Hopefully at the end of this article, things will become more clear than they were at the beginning of this article.

  • as Operator

As operator is used to convert one type to another when compiler is guaranteeing the success of desired cast. With this operator, the source and destination are convertible to each other, and if not compiler will produce an error asking to avoid the incompatible cast.

However, if you really want to force cast, you can use optional cast operators as? or as! which will produce successful casted value if cast is successful, otherwise will produce nil value and cause crash respectively.

as is an old operator. This can be used to convert primitive types which are interchangeable or converting Swift types to Objective-C and vice versa.

For example, following conversions are implicitly allowed by Swift by just using as keyword. No fancy as? or as! are required

let doubleVal = 0.0 as Double
let ObjCString = "asd" as NSString
let ObjCURL = URL(string: "") as NSURL?
let anyObj = 3 as AnyObject
let anyVal_1 = 3 as Any
let ObjCArray = [] as NSArray
let ObjCDictionary = [:] as NSDictionary
let floatVal = 0.0 as Float

Please note that as keyword cannot be used for conversions such as direct from Double to Int. It will result in error

double_to_int_conversion_error

  • as? Operator

This operator is more sophisticated that is allows you to check if object of one type is castable to another. If cast is successful, it will return the casted value, otherwise returns nil.

Let's understand this keyword by the use of class hierarchy since this is where type conversions come handy.

I am going to use the same inheritance for next two operators, so you might want to look at this example as a base for further sample codes

class Developer {
    func printWhoAmI() {
        print("I am Developer")
    }
}

class Android: Developer {
    override func printWhoAmI() {
        print("I am an Android developer")
    }
}

class iOS: Developer {
    override func printWhoAmI() {
        print("I am an iOS developer")
    }
}

class CSharp: Developer {
    override func printWhoAmI() {
        print("I am a C Sharp developer")
    }
}

let devs: [Developer] = [Developer(), Android(), iOS(), CSharp()]

Here we have Developer class as a base and 3 other classes, Android, iOS and C-Sharp are inherited from it. We are also maintaining an array of objects of type Developer.

Note that, array may not only contain Developer types, but also any classes inherit from Developer

Now, let's play with some examples

for dev in devs {
    // Code below prints each line once for every type. The base class Developer does not print anything since it is a superclass.

    // The line below will get executed for every object since all of them are either of type Developer or inherit from it

    if let andDev = dev as? Developer {
        print("Developer")
    }

    // The lines below will get printed only once for every conforming type

    if let andDev = dev as? Android {
        print("Casted to Android")
    }

    if let iOSDev = dev as? iOS {
        print("Casted to iOS")
    }

    if let cSharpDev = dev as? CSharp {
        print("Casted to C Sharp")
    }
}

// Output
Developer
Casted to Android
Developer
Casted to iOS
Developer
Casted to C Sharp

// Note that below code will produce nil, since we are trying to cast Android type to iOS

let iOSDev = devs[1] as? iOS

// So will this, since attempt is made to cast iOS type to Android

let androidDev = devs[1] as? Android

// This code will successfully cast Android object into Developer, since it's upcasting

let genericDeveloper = devs[1] as? Developer

  • as! Operator

as! operator is very similar to its sibling as?. Difference is as! is more evil.

Where as? will produce nil value is cast is unsuccessful, as! will result in crash if cast fails.

// Forcefully casting Android type to iOS. 
// This will crash since devs[1] is of type Android
let forcediOSDveloper = devs[1] as! iOS

// Forcefully casting iOS type to Android
let forcedAndroidDveloper = devs[2] as! Android

// This code will cast successfully
let validCast = devs[1] as! Android

As it's evident from screenshot below, the code above will result in crash for incompatible types.

crash_by_invalid_cast

However, if cast if possible it will return the casted object back

  • is Operator

is operator is slightly different from rest of the operators mentioned in this post. While other operators actually perform the cast, this operator will check if given object is of specified type of conforms to given protocol.

To elaborate more on this, let's make a protocol and make a class to conform to it

protocol Prot {
    func doit()
}

protocol AnotherProt {
    func doitToo()
}

class Animal: Prot {
    func doit() {

    }
}

let animal = Animal()

if animal is Prot {
    print("Conforms to Prot")
}

if animal is Animal {
    print("Class is of type Animal")
}

if animal is AnotherProt {
    // This will not print since animal does not conform to type AnotherProt
    print("Class is of type AnotherProt")
}

// Past example with devs array
// let devs: [Developer] = [Developer(), Android(), iOS(), CSharp()]

for dev in devs {
    if dev is CSharp {
        print("Dev is C Sharp")
    } else if dev is iOS {
        print("Dev is iOS")
    } else if dev is Android {
        print("Dev is Android")
    } else {
        print("Default Developer")
    }
}

// Output of previous code
Default Developer
Dev is Android
Dev is iOS
Dev is C Sharp

As it is clear from above example, whenever object O is of type T or inherit from T, or conforms to protocol P, following statements are true

if O is T {

}

if O is P {
    
}




References and Further reading:

Apple Developer Portal

Up casting and down casting confusion in Swift

Jayesh Kawli

I am a web and mobile developer working at Wayfair in Boston, MA. I come to learn so many things during course of life and I write about things which helped me and feel like they can help others too.

Subscribe to Fresh Beginning

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!