Uplift iOS Interview
The Guide is for YOU if
- You are preparing for an iOS interview and want to improve your skills and knowledge and looking to level up your interview game and land your dream job.
- You want to gain confidence and ease during iOS interviews by learning expert tips and curated strategies.
- You want access to a comprehensive list of iOS interview QA to practice and prepare.
In Swift, type erasure is a technique used to abstract away the concrete type of a value and replace it with a protocol that describes the behavior of the value. This allows you to work with values in a generic way, without having to know the specific types of those values at compile time.
Type erasure is often used when you have a collection of values with different concrete types that all share a common behavior. By creating a protocol that describes that behavior and implementing it for each concrete type, you can create a collection of values that all conform to the protocol. You can then use the protocol to work with the values in the collection in a generic way, without having to know their concrete types.
Here’s an example. Let’s say you have a collection of shapes:
enum Shape { case circle(radius: Double) case rectangle(width: Double, height: Double) case triangle(base: Double, height: Double) }
You want to calculate the area of each shape in the collection, but you don’t want to write separate code for each shape type. Instead, you can create a protocol that describes the behavior of shapes that have an area:
protocol HasArea { var area: Double { get } }
You can then implement the protocol for each concrete shape type:
extension Shape: HasArea { var area: Double { switch self { case .circle(let radius): return Double.pi * radius * radius case .rectangle(let width, let height): return width * height case .triangle(let base, let height): return 0.5 * base * height } } }
Finally, you can use type erasure to create a collection of values that all conform to the HasArea
protocol:
struct AnyHasArea { var area: Double init<T: HasArea>(_ hasArea: T) { area = hasArea.area } } let shapes: [AnyHasArea] = [ AnyHasArea(Shape.circle(radius: 1.0)), AnyHasArea(Shape.rectangle(width: 2.0, height: 3.0)), AnyHasArea(Shape.triangle(base: 4.0, height: 5.0)) ] for shape in shapes { print(shape.area) }
In this example, the AnyHasArea struct uses type erasure to wrap any value that conforms to the HasArea protocol, regardless of its concrete type. This allows you to create a collection of values with different concrete types, but that all share the common behavior of having an area. You can then work with the values in the collection in a generic way, without having to know their concrete types.
Let’s take a look at another example:
protocol Vehicle { func start() } class Car: Vehicle { func start() { print("Starting car...") } } class Motorcycle: Vehicle { func start() { print("Starting motorcycle...") } } func startVehicle<T: Vehicle>(_ vehicle: T) { vehicle.start() } let car = Car() startVehicle(car) let motorcycle = Motorcycle() startVehicle(motorcycle)
In this example, we have a Vehicle
protocol and two classes that conform to it: Car
and Motorcycle
. We also have a generic function startVehicle
that takes any type that conforms to Vehicle
and calls its start
method.
Now, suppose we want to create a collection of Vehicle
objects and iterate over them, calling their start
method. We might try to do this:
let vehicles: [Vehicle] = [car, motorcycle] for vehicle in vehicles { startVehicle(vehicle) }
However, this will result in a compiler error: “Protocol ‘Vehicle’ can only be used as a generic constraint because it has Self or associated type requirements”. This is because the startVehicle
function is generic and expects a specific type, not a protocol with associated types.
To solve this problem, we can use type erasure to create a wrapper around the Vehicle
protocol that provides a common interface. Here’s one way to do this:
class AnyVehicle { private let _start: () -> Void init<T: Vehicle>(_ vehicle: T) { _start = vehicle.start } func start() { _start() } } let anyVehicles: [AnyVehicle] = [AnyVehicle(car), AnyVehicle(motorcycle)] for vehicle in anyVehicles { vehicle.start() }
In this example, we’ve created an AnyVehicle
class that takes a generic type T
that conforms to Vehicle
. The AnyVehicle
class stores a closure that captures the start
method of the underlying object. We can then create an array of AnyVehicle
objects and iterate over them, calling their start
method. The specific type of each object is erased, allowing them to be treated as a single type.
✍️ Written by Ishtiak Ahmed
👉 Follow me on X ● LinkedIn
Get Ready to Shine: Mastering the iOS Interview
Enjoying the articles? Get the inside scoop by subscribing to my newsletter.
Get access to exclusive iOS development tips, tricks, and insights when you subscribe to my newsletter. You'll also receive links to new articles, app development ideas, and an interview preparation mini book.