Conditional Casting with as?
Attempt a cast that returns an optional on failure.
Conditional Casting with as? is a free Swift Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Swift Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Conditional Casting
The conditional cast operator as? attempts a downcast and returns an optional: the value wrapped if it succeeds, or nil if it fails.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let maybe = a as? Dog
print(maybe != nil)Result Is Optional
Because the cast might fail, the result type is Dog?, not Dog. You must unwrap it before use.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let maybe: Dog? = a as? Dog
print(type(of: maybe))Unwrapping with if let
The idiomatic way to use as? is inside an if let binding, which runs the body only when the cast succeeds.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
if let dog = a as? Dog {
print(dog.bark())
} else {
print("Not a dog")
}Failed Cast Returns nil
When the instance is not the target type, as? yields nil and the if let body is skipped.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Cat(name: "Mia")
if let dog = a as? Dog {
print(dog.bark())
} else {
print("That is not a dog")
}Combining with guard let
You can also use guard let to cast early and exit if the cast fails, keeping the happy path unindented.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
func describe(_ a: Animal) {
guard let cat = a as? Cat else {
print("Not a cat")
return
}
print(cat.meow())
}
describe(Cat(name: "Mia"))
describe(Dog(name: "Rex"))Conditional Cast in Loops
Iterating a mixed array and conditionally casting each element is a clean way to handle different subclasses safely.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let zoo: [Animal] = [Dog(name: "Rex"), Cat(name: "Mia"), Dog(name: "Buddy")]
for a in zoo {
if let dog = a as? Dog {
print(dog.bark())
}
}Casting to Protocols
as? also tests protocol conformance: it returns a value if the instance conforms to the protocol, otherwise nil.
protocol Greeter { func greet() -> String }
class Person: Greeter { func greet() -> String { return "Hi" } }
class Rock {}
let items: [Any] = [Person(), Rock()]
for item in items {
if let g = item as? Greeter { print(g.greet()) }
}Nil-Coalescing After a Cast
You can pair as? with the ?? operator to provide a fallback when the cast fails.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Cat(name: "Mia")
let sound = (a as? Dog)?.bark() ?? "unknown sound"
print(sound)Chaining Optional Cast Calls
Optional chaining lets you call a method on the cast result; the whole expression is nil if the cast fails.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let bark = (a as? Dog)?.bark()
print(bark as Any)Why Conditional Casting Is Preferred
as? never crashes; a failed cast simply produces nil. This makes it the safe default for downcasting.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let unknown: Animal = Cat(name: "Mia")
let result = unknown as? Dog
print("Crashes? No. Value is nil:", result == nil)Conditional Cast Summary Example
Putting it together: detect a subtype safely, act on it, and fall back gracefully otherwise.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
func sound(of a: Animal) -> String {
if let d = a as? Dog { return d.bark() }
if let c = a as? Cat { return c.meow() }
return "silence"
}
print(sound(of: Dog(name: "Rex")))
print(sound(of: Cat(name: "Mia")))Quick Check
Recall the result type of a conditional cast.
Recap: Conditional Casting with as?
You learned that as? performs a safe downcast returning an optional, that if let and guard let unwrap the success case, and that it never crashes because failure simply yields nil, making it the preferred downcast operator.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let pets: [Animal] = [Dog(name: "Rex"), Cat(name: "Mia")]
for p in pets {
if let d = p as? Dog { print(d.bark()) } else if let c = p as? Cat { print(c.meow()) }
}Frequently asked questions
Is the “Conditional Casting with as?” lesson free?
Yes — the full text of “Conditional Casting with as?” is free to read here on the web, and the Swift Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Swift Academy course, upgrade to CoddyKit PRO.
What will I learn in “Conditional Casting with as?”?
Attempt a cast that returns an optional on failure. You practise Swift Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Swift Academy?
No prior experience is required. Swift Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Conditional Casting with as?” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Swift Academy lesson?
Yes. Every Swift Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Upcasting and Downcasting Basics
- Conditional Casting with as?
- Forced Casting with as! and Its Risks
- Checking Types with is