๐ŸŽ“ A1Learner
๐Ÿ”
0 of 36 lessons completed

Swift Type Casting

Type casting checks the real type of a value at run time. It can also view a value as a more specific type, with a result the code must test.

Counting types with is

The animals array holds dogs and cats under the Animal type. The loop counts each kind with the is operator.

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Dog: Animal {
}
class Cat: Animal {
}
let animals: [Animal] = [Dog(name: "Pip"), Cat(name: "Moss"), Dog(name: "Rex")]
var dogs = 0
var cats = 0
for animal in animals {
    if animal is Dog {
        dogs += 1
    } else if animal is Cat {
        cats += 1
    }
}
print(dogs)
print(cats)

Downcasting with as?

The fetch method exists only on Dog. The if let binding succeeds for each dog, so both fetch lines print and the cat is skipped.

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Dog: Animal {
    func fetch() -> String {
        return "\(name) fetches the ball"
    }
}
class Cat: Animal {
}
let animals: [Animal] = [Dog(name: "Pip"), Cat(name: "Moss"), Dog(name: "Rex")]
for animal in animals {
    if let dog = animal as? Dog {
        print(dog.fetch())
    }
}

A failed downcast gives nil

The animal value holds a Cat, so the cast to Dog fails. The else branch prints a plain line instead of a fetch line.

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Dog: Animal {
    func fetch() -> String {
        return "\(name) fetches the ball"
    }
}
class Cat: Animal {
}
let animal: Animal = Cat(name: "Moss")
if let dog = animal as? Dog {
    print(dog.fetch())
} else {
    print("Not a dog")
}

Example

class Instrument {
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Drum: Instrument {
    func beat() -> String {
        return "\(name) goes boom"
    }
}
class Flute: Instrument {
}
let band: [Instrument] = [Drum(name: "Big drum"), Flute(name: "Flute"), Drum(name: "Hand drum")]
var drums = 0
for item in band {
    if item is Drum {
        drums += 1
    }
    if let drum = item as? Drum {
        print(drum.beat())
    }
}
print(drums)

Key points

  • An array typed as a base class can hold objects of its subclasses.
  • The is operator returns true when a value belongs to the checked type.
  • The as? operator tries a downcast and gives an optional result.
  • A failed as? downcast gives nil instead of a value.
  • Binding an as? result with if let gives access to subclass members.
  • A subclass method can be called only after a successful downcast.

In this example, the band array mixes Drum and Flute objects under the Instrument type. The is check counts two drums during the loop. The as? binding then calls the Drum-only beat method, so both drum lines print before the count.

Note

A downcast does not change the object itself, only the type used to look at it.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

An as? cast from Animal to Dog is applied to a value that holds a Cat. What does the cast give?