Type Casting (is, as?)
Discover and convert types at runtime: check a type with is , safely downcast with as? , force a cast with as! , and work with the catch-all Any and AnyObject types.
Learn Type Casting (is, as?) in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- • Check an instance's type with is (returns a Bool )
- • Safely downcast with as? (returns an optional)
- • Force a downcast with as! — and know it can crash
- • Use Any and AnyObject for mixed values
- • Downcast inside a switch with case let x as T:
- • Choose the right operator for each situation
1️⃣ Checking Types with is
The is operator answers a yes/no question: is this instance of that type? It returns a Bool , so it's perfect for counting or filtering without converting anything.
2️⃣ Downcasting with as? and as!
Downcasting converts a general type to a more specific one. The safe form, as? , returns an optional — the value on success, nil on failure — so pair it with if let . The forced form, as! , returns the value directly but crashes if the cast fails, so only use it when you're certain.
3️⃣ Any / AnyObject and Downcasting in a switch
Any can hold a value of any type; AnyObject holds only class instances. To work with a mixed collection, discover each element's real type by downcasting inside a switch using case let x as Type: .
Your turn. Fill in the type-check and conditional-cast operators.
Pro Tips
- 💡 Default to as? . Pair it with if let or guard let to handle the miss.
- 💡 Reserve as! for casts you can prove — a wrong guess crashes instantly.
- 💡 switch + case let x as T is the cleanest way to handle an Any .
- 💡 Prefer protocols/generics over casting when you can — needing lots of casts often hints at a design you can simplify.
Common Errors (and the fix)
- "Could not cast value of type ... " crash: an as! cast failed. Switch to as? and handle nil .
- Forgetting the result of as? is optional: bind it with if let before using it.
- "'is' test is always true": a warning that the check is redundant — the value is already that type.
- Using AnyObject for a struct/Int: value types need Any ; AnyObject is class-only.
📋 Quick Reference
Operator
Code
Returns
Type check
x is Dog
Bool
Safe cast
x as? Dog
Dog? (nil on fail)
Forced cast
x as! Dog
Dog (crash on fail)
Upcast
dog as Animal
always succeeds
Mixed values
[Any]
any type
In switch
case let n as Int:
binds n
Frequently Asked Questions
Mini-Challenge: Shape Describer
No blanks this time — just a brief and an outline. Use a switch with downcasting cases to describe each shape.
Practice quiz
What does the 'is' operator check?
- Whether a value can be cast and returns the cast value
- Whether two values are equal
- Whether an instance is of a certain type, returning a Bool
- Whether a value is nil
Answer: Whether an instance is of a certain type, returning a Bool. 'is' is the type-check operator: it returns true or false depending on the instance's type.
What does the 'as?' operator return?
- An optional — the cast value if it succeeds, or nil if it fails
- The cast value, or a crash on failure
- Always nil
- A Bool
Answer: An optional — the cast value if it succeeds, or nil if it fails. 'as?' is the conditional cast; it returns an optional that is nil when the cast can't succeed.
What happens with 'as!' if the cast fails at runtime?
- It returns nil
- It returns false
- It returns the original value
- It crashes the program
Answer: It crashes the program. 'as!' is the forced cast — it crashes if the downcast is not actually possible.
Which cast is safest to use when you are not certain of the type?
- as!
- as?
- is
- as
Answer: as?. Use 'as?' (conditional cast) when unsure; it returns nil instead of crashing.
What is the difference between Any and AnyObject?
- Any can hold any type including value types; AnyObject only holds class instances
- They are identical
- AnyObject holds any type; Any only holds classes
- Any only holds Ints
Answer: Any can hold any type including value types; AnyObject only holds class instances. Any represents an instance of any type at all; AnyObject represents an instance of any class type.
When downcasting in a switch statement, which keyword pattern is used?
- is only
- case x is SomeType:
- case let x as SomeType:
- cast SomeType
Answer: case let x as SomeType:. In a switch you write 'case let x as SomeType:' to bind the downcast value.
Is 'as' (without ? or !) ever used, and for what?
- Never
- For guaranteed conversions like upcasting or bridging that always succeed
- Only for optionals
- Only in switch
Answer: For guaranteed conversions like upcasting or bridging that always succeed. Plain 'as' is for casts the compiler knows always succeed, such as upcasting to a superclass.
Given 'let things: [Any] = [1, "hi", 3.0]', how do you safely treat an element as a String?
- thing as String
- thing as! Int
- thing is String only
- if let s = thing as? String { }
Answer: if let s = thing as? String { }. 'as?' with optional binding safely extracts the String when the element actually is one.
What does 'value is String' evaluate to when value holds an Int?
- true
- false
- nil
- It crashes
Answer: false. 'is' returns false when the instance is not of the checked type.
Why use 'as?' over 'as!' for values from a heterogeneous collection?
- as? is faster
- as! returns nil
- as? lets you handle the non-matching case gracefully instead of crashing
- There is no difference
Answer: as? lets you handle the non-matching case gracefully instead of crashing. With mixed types you can't guarantee the cast, so as? avoids the crash that as! would cause on a mismatch.
Continue this course
- Previous: Optional Chaining
- Next: Access Control