Swift Type Casting
Type casting lets you convert between related types or check a value's runtime type, using the as, as?, and as! operators to match the level of certainty you actually have.
Why Type Casting Exists
Swift is strict about types: an Int and a Double cannot be mixed in the same expression, and a value typed as Any could be hiding any concrete type underneath. Type casting is how you bridge that gap — either by converting a numeric value from one type to another, or by asking a class instance what its actual runtime type is.
Numeric conversion
let wholeNumber: Int = 7
let asDouble = Double(wholeNumber) // 7.0
let decimal: Double = 9.8
let asInt = Int(decimal) // 9, truncated, not roundedas, as?, and as!
When working with class hierarchies, Swift gives you three casting operators. Plain as performs a cast that's guaranteed to succeed, typically when casting up to a known superclass or protocol. as? is the optional cast — it returns nil instead of crashing if the cast fails, so it's the safest choice when you're not certain of a value's type. as! is the forced cast — it assumes success and crashes at runtime if it's wrong, so it should only be used when you are absolutely certain of the type.
Safe vs forced casting
class Animal {}
class Dog: Animal { func bark() { print("Woof!") } }
let pet: Animal = Dog()
if let dog = pet as? Dog {
dog.bark() // safe: runs only if the cast succeeds
} else {
print("Not a dog")
}
let forcedDog = pet as! Dog // crashes here if pet isn't actually a Dog
forcedDog.bark()Casting with Any and AnyObject
Collections typed as [Any] can hold values of completely different types, and casting is how you recover their real type before using type-specific behavior. This pattern shows up often when working with heterogeneous arrays or data parsed from JSON.
Casting inside a loop
let mixedValues: [Any] = [1, "two", 3.0, true]
for value in mixedValues {
if let number = value as? Int {
print("Int: \(number)")
} else if let text = value as? String {
print("String: \(text)")
} else {
print("Other: \(value)")
}
}- as — upcasting that always succeeds, checked at compile time.
- as? — safe downcasting that returns an optional, nil on failure.
- as! — forced downcasting that crashes the app if the cast fails.
- Int(), Double(), String() initializers convert between concrete value types.
Choosing the Right Operator
Exercise: Swift Type Casting
What does the as? operator return when a downcast fails?