Type Erasing in Swift

Chanakya Hirpara
2 min readJun 13, 2023

In Swift, type erasure is a powerful technique that allows you to abstract away specific types and work with protocols instead. It enables you to hide implementation details and create more flexible, generic code. Let’s explore type erasure in Swift using a realtime example.

Type erasure is particularly useful when dealing with protocols that have associated types. Associated types allow for generic behavior, but they can make it challenging to work with heterogeneous collections or pass instances around without knowing their specific types. Type erasure solves this problem by introducing an intermediate type that erases the specifics and exposes only the common behavior defined by a protocol.

Let’s say we have a protocol called Drawable that represents any object that can be drawn:

protocol Drawable { func draw() }

Now, imagine we have different concrete types that conform to the Drawable protocol, such as Circle, Square, and Triangle. Each of these types has its own drawing implementation.

struct Circle: Drawable { 
func draw() { // Drawing logic for a circle
}
}

struct Square: Drawable {
func draw() { // Drawing logic for a square
}
}

struct Triangle: Drawable {
func draw() { // Drawing logic for a triangle
}
}

While working with an array of Drawable instances, we might encounter difficulties due to the associated types. We cannot create a homogeneous array of Drawable instances because they have different associated types.

This is where type erasure comes to the rescue. We can create a type-erased wrapper called AnyDrawable that erases the specific type and exposes only the common behavior:

struct AnyDrawable: Drawable { 
private let _draw: () -> Void

init<T: Drawable>(_drawable: T) {
_draw = drawable.draw
}

func draw() {
_draw()
}
}

The AnyDrawable type erases the specific type and wraps any Drawable instance. It takes advantage of Swift's closures to store the draw() implementation of the underlying instance.

Now, we can create an array of AnyDrawable instances and work with them uniformly:

let shapes: [AnyDrawable] = [ 
AnyDrawable(Circle()),
AnyDrawable(Square()),
AnyDrawable(Triangle())
] // Iterate and draw each shape for shape in shapes { shape.draw() }

With type erasure, we can treat all the shapes in the shapes array uniformly, regardless of their underlying types. The draw() method is invoked correctly on each shape, thanks to the type-erased AnyDrawable.

Conclusion: Type erasure is a powerful technique in Swift that allows you to work with protocols and hide implementation details. By creating type-erased wrappers, you can abstract away specific types and work with homogeneous collections or pass instances around without knowing their exact types. This promotes code reusability, flexibility, and generic programming.

In our example, we used type erasure to create an AnyDrawable wrapper that erased the specifics of Circle, Square, and Triangle instances and exposed only the common behavior defined by the Drawable protocol. This allowed us to work with them uniformly and invoke the draw() method correctly on each shape.

By understanding and applying type erasure in your code, you can design more flexible and reusable components in your Swift projects.

Please clap, share and follow for more knowledge like this.

--

--