Type casting in swift : difference between is, as, as?, as!

What is the difference between is, as, as?, as! in swift? Well, lets check.

Apple doc says: Type casting is a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy.

Type casting in Swift is implemented with the is and as operators. is is used to check the type of a value whereas as is used to cast a value to a different type.

Consider the following classs LivingBeing and two subclasses of LivingBeing named Human and Animal .

Now create a constant array called livingBeingArray with one Animal class object and one human class object. What do you think the type of this array created via type inference? It will be of type [LivingBeing] .

Swift’s type checker is able to deduce that Human and Animal have a common superclass of LivingBeing, and so it infers a type of [LivingBeing] for the livingBeingArray array.

The items stored in livingBeingArray are still Human and Animal instances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed as LivingBeing, and not as Human or Animal. In order to work with them as their native type, you need to check their type, or downcast them to a different type.

Checking Type

Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.

Consider the following code:

let livingBeingObj = livingBeingArray[0] // returns a LivingBeing object.

Let’s iterate the array objects over a for loop.

for item in livingBeingArray {
if item is Animal {
print("item is of type Animal")// will get executed for first item
} else if item is Human {
print("item is of type Human")// will get executed for second item
}
}

Downcasting

Apple doc says: A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).

Let’s simplify this. Consider the array livingBeingArray . We know that the first item is of type Animal . Since array contains one Animal object and one Human object, the type inference will decide the array type as LivingBeing. If we try to get any content from this array, it will return you an object of type LivingBeing.In that case we can try to downcast it after fetching it form the array.

Difference between as? and as!

Downcasting can be done in two ways:

  • Conditional downcasting (as?).
  • Forced downcasting (as!).

The conditional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action.

Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.

Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.

Here in the above scenario, since we know that the first object in the array is of type Animal, we can use Forced downcasting.

let animalObj = livingBeingArray[0] as! Animal //forced downcasting to Animal
let humanObj = livingBeingArray[1] as! Human //forced downcasting to Human

But forced downcasting can fail if we try to downcast the first object to a Human and second object to an Animal. In this case the result will be nil which a normal type cannot handle and the program will crash.

let animalObj = livingBeingArray[0] as! Human //error and crashes
let humanObj = livingBeingArray[1] as! Animal //error and crashes

In this scenario, where we are not sure if the casting succeeds, we should use the conditional downcasting as? .

let animalObj = livingBeingArray[0] as? Human //nil..animalObj is of Human? (optional Human which is the type which we tried to downcast to)
let humanObj = livingBeingArray[1] as? Animal //nil..humanObj is of Animal? (optional Animal which is the type which we tried to downcast to)

But conditional downcasting of the correct type succeeds and returns the correct optional type that we are trying to downcast to.

let animalObj = livingBeingArray[0] as? Animal // success, returns Animal?
let humanObj = livingBeingArray[1] as? Human // success, returns Human?

Upcasting

Upcasting from the base class object to its superclass is also possible. Let’s convert the animalObject created by forced downcasting back to the LivingBeing class.

let animalObj = livingBeingArray[0] as! Animal
let animalObjectAsLivingBeingObj = animalObj as LivingBeing

animalObjectAsLivingBeingObj is of type LivingBeing .

Type Casting for Any and AnyObject

Swift provides two special types for working with nonspecific types:

  • Any can represent an instance of any type at all, including function types.
  • AnyObject can represent an instance of any class type.
Source: Internet

The keyword ‘Any’ is used to represent an instance which belongs to any type including function types. Consider an array of type Any which can accept different types of values. We can use a switch statement to checl the type and do downcasting.

var groups = [Any]()
groups.append(1.0)
groups.append(1)
groups.append("string")

for item in groups {
switch item {
case let anInt as Int:
print("\(item) is an int")
case let aDouble as Double:
print("\(item) is a double")
case let aString as String:
print("\(item) is a string")

default:
print("dunno")
}
}

/*
1.0 is a double
1 is an int
string is a string
C11lldb_expr_13Pop (has 1 child) is a Genre
*/
Forced and conditional downcasting will not work in a switch-case

Using switch statement for type checking and downcasting :

for obj in livingBeingArray
{
switch obj {
case let animalObj as Animal:
print(“\(obj) is an animal”)
break
case let humanObj as Human:
print(“\(obj) is an human”)
break
default:
print(“unknown type”)
}
}
you may also use is operator for type checking.
for obj in livingBeingArray
{
switch obj {
case is Animal:
print(“\(obj) is an animal”)
break
case is Human:
print(“\(obj) is an human”)
break
default:
print(“unknown type”)
}
}

Source: AppleDocs

Enjoy!!

If you enjoyed reading this post, please share and recommend it so others can find it 💚💚💚💚💚💚 !!!!