ProgrammingiOS Developer, Middle/Senior

What is type erasure in Swift, why is it needed, and how to properly implement it in practice?

Pass interviews with Hintsage AI assistant

Answer.

Background: Type erasure is a pattern that emerged in Swift as a response to the limitations of protocols with associated types or Self requirements. A protocol cannot be used as a type directly without type erasure if it contains associated types, because the compiler does not know the specific implementation. This is often encountered when creating data containers, collections, streams, where abstraction is important.

Problem: If there is a protocol with an associated type, for example:

protocol Animal { associatedtype Food func eat(_ food: Food) }

you cannot declare a variable of type Animal. We need a "type-erasing wrapper" that allows accessing different concrete types through abstraction:

let zoo: [any Animal] // Compilation error

Solution: Type erasure is implemented using wrappers (for example, Box pattern or struct AnyXxx), providing a uniform interface while hiding the details of the actual type:

struct AnyAnimal<F>: Animal { private let _eat: (F) -> Void init<A: Animal>(_ base: A) where A.Food == F { _eat = base.eat } func eat(_ food: F) { _eat(food) } }

Now we can store different implementations of Animal with the same Food:

let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]

Key features:

  • Allows working with protocols with associated types as if they were ordinary types
  • Solves the problem of universal containers and factories for protocol entities
  • Serves as an architectural pattern for hiding implementation details

Trick questions.

Can you use a protocol with an associated type without type erasure as a property type or array?

No, you cannot. The compiler requires specification of all associated types. For example, the following declaration will cause an error:

let array: [Animal] // Error: 'Animal' can only be used as a generic constraint

Can type erasure replace inheritance?

No, type erasure is not a complete replacement for inheritance. It addresses the abstraction problem for protocols with associated types, while inheritance is used to implement common logic and code reuse among classes.

Do all methods and properties of a protocol need to be implemented inside the type erasure wrapper?

Yes, they must. Type erasure works only if the wrapper fully replicates the external interface of the protocol; otherwise, some functionality will be unavailable.

Common mistakes and anti-patterns

  • Forgetting to implement all methods/properties, leading to silent loss of functionality
  • Using type erasure where it could have been avoided (e.g., where there are no associated types)
  • Creating redundant or overly complex wrappers that reduce readability

Real-life example

Negative case

In a production project, all work with data sources is built around type erasure, even when there is no need for associated types. The code becomes hard to maintain, and new developers get confused.

Pros:

  • Versatility of the API

Cons:

  • Reduced readability, increased complexity of the architecture, latent bugs

Positive case

Type erasure is applied only for abstraction of the data source, which encapsulates different loading strategies (network, local), each with its own types. Otherwise, the code is transparent.

Pros:

  • Flexibility of the architecture, simplicity of integrating new strategies

Cons:

  • Adds a layer of intermediaries (wrappers), which slightly complicates debugging