Logo for tanaschita.com

Understanding existentials and primary associated types in Swift

How existentials and primary associated types enhance flexibility in protocols.

17 Mar 2025 · 5 min read

Existentials are useful when working with protocols that have associated types. In Swift, protocols with associated types cannot be used as standalone types because their concrete type is unknown at compile time. To work around this, Swift allows us to use existentials (any Protocol) to enable type erasure and store different conforming types in a single collection.

Let's explore how they work in practice.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

Let's consider an example where we'll create a system to manage different geometric shapes such as circles, rectangles or triangles and calculate their areas. Each shape conforms to a common protocol Shape which defines a method for calculating the area:

protocol Shape {
associatedtype Parameters
func area(with parameters: Parameters) -> Double
}

Here, the Parameters associated type will be different for each shape. For example, the parameters for a circle might be the radius, while the parameters for a rectangle could be its width and height.

Implementing concrete shapes

Let's define two shape implementations: Circle and Rectangle.

struct Circle: Shape {
struct CircleParameters {
let radius: Double
}
func area(with parameters: CircleParameters) -> Double {
return Double.pi * parameters.radius * parameters.radius
}
}
struct Rectangle: Shape {
struct RectangleParameters {
let width: Double
let height: Double
}
func area(with parameters: RectangleParameters) -> Double {
return parameters.width * parameters.height
}
}

Following the same structure, we could define more shapes, such as Triangle.

Storing shapes in a collection

If we try to store different shapes in an array using the Shape protocol directly, we run into a compiler error:

// Compiler error: Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements.
var shapes: [Shape] = [Circle(), Rectangle(), Triangle()]

Swift does not allow storing values of a protocol with associated types in a collection unless their concrete types are known at compile time. This is where where existentials come into play:

var shapes: [any Shape] = [Circle(), Rectangle(), Triangle()]

Now, Swift allows different implementations to coexist in the same array, but with the trade-off of losing static type information. This means we don't know the specific type of each shape at compile time. To work around this, we must cast the shapes back to their concrete types before calling their methods.

let circleParameters = Circle.CircleParameters(radius: 10)
let rectangleParameters = Rectangle.RectangleParameters(width: 5, height: 8)
for shape in shapes {
if let circle = shape as? Circle {
print("Circle area: \(circle.area(with: circleParameters))")
} else if let rectangle = shape as? Rectangle {
print("Rectangle area: \(rectangle.area(with: rectangleParameters))")
}
}

While this works, it introduces runtime type checking and casting, which can be error-prone.

Using primary associated types

To reduce the need for type casting, Swift provides primary associated types, allowing us to specify associated types explicitly when working with existentials:

protocol Shape<Parameters> {
associatedtype Parameters
func area(with parameters: Parameters) -> Double
}

Now, we can use Shape<CircleParameters> to define a concrete type when working with the protocol. However, for heterogeneous collections, we still need to use any without a concrete associated type.

While primary associated types help restore lost type information, they do not introduce additional trade-offs when used with any. Instead, they extend the flexibility of existentials, allowing us to specify associated types when needed while still leveraging any for heterogeneous collections.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

generics and protocols

swift

Understanding opaque types and protocols with associatedtype in Swift

Learn how to leverage the some keyword for working with protocols with associated types.

06 Jan 2025 · 2 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum