Explaining Curiously Recurring Template Pattern in Swift

Let’s talk about CRTP in Swift!

Ihor Malovanyi
5 min readDec 20, 2023

As a developer, you use some of the most common design patterns daily (сonsciously or not). But I want to explain to you one hidden but effective design pattern.

Curiously Recurring Template Pattern

The concept was described first in 1989 as F-bounded polymorphism. The primal idea is recursive type definitions and subtyping in object-oriented programming.

If we view objects as elements of non-recursive record types, then bounded quantification provides a useful form of polymorphism over objects.

CRTP is based on F-bounded polymorphism — the implementation seems to be upside-down inheritance due to the implementation allowing the change of a base class using inheritance generics.

Let’s dive deep and see the core concepts of CRTP with Swift!

If you like my content and want to support me, please buy me a coffee and subscribe!

Also, I have the Telegram channel Swift in UA, where you can find more exciting things.

CRTP Core with Swift

The implementation of CRTP with Swift is very clear:

class Base<Deriver: AnyObject> {

}

class SomeDeriver: Base<SomeDeriver> {

}

But only the implementation is nothing without the features it provides! The two primary constructions are Polymorphism Chaining and Polymorphism Copy.

Polymorphism Chaining

Polymorphism Chaining refers to the ability to chain methods across subclasses in a polymorphic way. This is particularly useful when subclasses add their own methods and you want to call these methods in a chain, while maintaining the specific subclass type.

The CRTP pattern allows for Polymorphism Chaining by using generic types in the base class, ensuring that methods return the type of the subclass.

class Base<T: AnyObject> {
func baseMethod() -> T {
//... impl
return self as! T
}
}

class Sub: Base<Sub> {
func subMehod() -> Self {
//... impl
return self
}
}

let sub = Sub()

sub
.baseMethod()
.subMehod()

In this example, baseMethod in the Base class and subMethod in Sub can be chained together. The return type of baseMethod is T, which is Sub in this context, allowing for the chaining of subMethod.

Polymorphism Copy

Polymorphism Copy in Swift refers to the ability to copy an instance of a polymorphic type while preserving its dynamic type. In other words, when you copy an instance of a subclass, the copy should also be an instance of that subclass, not just an instance of the base class.

Swift’s standard Copy protocol can’t directly handle this scenario, as it doesn’t inherently support polymorphic copying. The CRTP can be a workaround to this limitation.

class Base<T: AnyObject>: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
fatalError("Subclass should implement copy")
}

func clone() -> T {
fatalError("Subclass should implement clone")
}
}

class Sub: Base<Sub> {
var value: String

init(value: String) {
self.value = value
}

override func clone() -> Sub {
Sub(value: value)
}

override func copy(with zone: NSZone? = nil) -> Any {
clone()
}
}

let original = Sub(value: "Hello")
let copied = original.copy() as! Sub

In this example, Sub overrides the clone method to return an instance of Sub. This ensures that the copy method returns the correct type.

Limitations

  1. You can use only AnyObject as a generic parameter in a base class. Otherwise, there will be a compiler error.
  2. You must use force unwrapping on the base-class level to ensure the chaining works. It is your responsibility to guarantee the pattern was used correctly.

Examples

Custom View Controllers

Imagine you’re building an application with several view controllers, each requiring a similar setup or lifecycle handling but with specifics to each controller. Here’s how CRTP can be leveraged:

class BaseViewController<T: AnyObject>: UIViewController {
func setup() -> T {
// Common setup logic
return self as! T
}

func addLifecycleLogging() -> T {
// Add logging for lifecycle events
return self as! T
}
}

class ProfileViewController: BaseViewController<ProfileViewController> {
func customizeProfileView() -> ProfileViewController {
// Specific customization for profile
return self
}
}

let profileVC = ProfileViewController()
.setup()
.addLifecycleLogging()
.customizeProfileView()

Custom Networking Layer

Suppose you’re creating a networking layer where each network request might return a specific type of response. CRTP can ensure that each request returns the correct type.

class NetworkRequest<T: AnyObject, ResponseType> {
func execute() -> ResponseType {
// Execute the request and return the response
// Placeholder for actual network logic
return response
}
}

class UserFetchRequest: NetworkRequest<UserFetchRequest, User> {
// Implementation specific to fetching a user
}

let request = UserFetchRequest()
let user = request.execute() // `user` is of type `User`

Custom Animations

In a scenario where you have a series of animations that share common properties but differ in certain aspects, CRTP can be used to chain animation configurations fluently.

class BaseAnimation<T: AnyObject> {
func start() -> T {
// Start the animation
return self as! T
}

func setDuration(_ duration: TimeInterval) -> T {
// Set the duration of the animation
return self as! T
}
}

class FadeAnimation: BaseAnimation<FadeAnimation> {
func setOpacity(_ opacity: Float) -> FadeAnimation {
// Set the opacity for fade animation
return self
}
}

let fadeAnimation = FadeAnimation()
.setDuration(0.5)
.setOpacity(0.8)
.start()

Conclusion

The use of the CRTP in Swift, while not as common as other generic patterns, offers a powerful tool for designing flexible and type-safe APIs, especially in frameworks like UIKit. It allows for more specific type handling in superclass methods without downcasting or type checking. However, using this pattern judiciously is essential, as it can introduce complexity into your codebase.

Practical implications of CRTP:

  • Specificity: CRTP allows methods in the superclass to return or operate on instances of the subclass, ensuring that the type information is preserved. This is particularly useful in frameworks like UIKit, where specific types of objects (like UI elements) often need to be manipulated or accessed in a type-safe manner.
  • Reusable Components: By using CRTP, you can create highly reusable components. For example, a generic network manager class could return specific types of responses tailored to the subclass, reducing the need for type casting and improving code readability.
  • Maintainability: This pattern can make the code more maintainable by encapsulating common logic in the base class and minimizing duplicate code across subclasses.
  • Custom UI Components: If you’re creating a set of custom UI components that share standard functionality but need to return or handle their specific types, CRTP can be a great fit. For example, a custom view class must chain configuration methods specific to its subclass.
  • Framework Development: When developing frameworks or libraries, especially those that interact with UIKit or similar Apple frameworks, CRTP can be used to provide a more fluent and type-safe API to the consumers of the framework.

--

--