Hello.
Over the years, the Swift community has gone through an incredible evolution. In the early days, many of us believed that classes might one day be replaced entirely by structs and protocols. However, as the language has matured, it’s become clear that classes still play a vital role in modern Swift development. From powerful features like @Observable
and frameworks like SwiftData, classes remain integral to building robust, flexible applications.
Today, I’d like to propose one more time a way to further empower class-based development in Swift by introducing the keywords virtual
, abstract
, and protected
. These concepts are familiar to developers from other object-oriented languages, and I believe they could add significant value to the Swift language.
Introduction
In the early days of Swift, there was a widespread belief that classes were on their way to being replaced by structs and protocols. This idea gained momentum due to Swift’s emphasis on safety, immutability, and avoiding the traditional pitfalls of reference types—like memory management issues associated with ARC. The notion of using value types (structs) and behavior contracts (protocols) to model most types of behavior seemed like a promising path, and many developers shifted their focus towards a more protocol-oriented design.
However, as the Swift ecosystem has matured, we’ve come to realize that classes are not only still relevant, but are indispensable in many modern Swift applications. Core features of Apple’s platforms, such as @Observable, Core Data, and SwiftData, are built upon classes and rely on class inheritance to function effectively. Classes remain critical when it comes to reference semantics, shared state, and the flexibility that comes with subclassing.
Given this, I propose we revisit some of the core concepts surrounding inheritance and encapsulation by introducing three important object-oriented features that are common in many other languages: virtual, abstract, and protected.
The Case for virtual
In object-oriented programming, the virtual keyword explicitly marks a method as one that can be overridden in a subclass. This would provide a clear signal to developers that a method is intended to be overridden in subclasses, as opposed to simply relying on inheritance by default, which can sometimes lead to misunderstandings or unintended behaviors.
Practical Examples in Apple SDKs:
viewWillAppear(_:)
inUIViewController
(UIKit): This method is commonly overridden to customize behavior just before a view appears on screen. With a virtual keyword, the intention to allow overriding would be explicit, helping to differentiate between methods that are designed to be customized and those that are not.layoutSubviews()
inUIView
(UIKit): This is another common override, where subclasses need to define their own layout logic. Marking this method as virtual would make it clear that subclassing for layout customization is expected.
The Case for abstract
An abstract class or method is one that cannot be instantiated directly and must be overridden or completed by a subclass. Swift currently lacks a formal way to declare such intent. Abstract classes or methods would provide a powerful tool for API designers to create more explicit and structured inheritance hierarchies.
In Swift today, this concept is often mimicked by creating protocols or by providing default implementations that raise fatal errors. While these are workarounds, they lack the clarity and expressiveness of an abstract keyword.
Practical Examples in Apple SDKs:
UITableViewDataSource
andUITableViewDelegate
(UIKit): These protocols require implementers to define certain methods, such astableView(_:cellForRowAt:)
. Although Swift uses protocols here, the concept of abstract methods in classes would allow similar patterns to be applied directly within class hierarchies, creating a clearer distinction between methods that must be implemented by subclasses.NSDocument
(AppKit): In macOS development,NSDocument
is often subclassed, and methods likewrite(to:ofType:)
need to be customized. These methods could be abstract, signaling that any subclass of NSDocument must provide its own logic for data serialization.
By making certain classes and methods explicitly abstract, we can help developers avoid confusion and provide a clearer intent about which parts of an API are extendable and which are meant to be customized.
The Case for protected
Protected methods and properties can be overridden or accessed by subclasses but not by instances of those subclasses or other external code. Swift currently lacks a formal protected access control level, which would improve encapsulation, especially when designing large frameworks or complex inheritance hierarchies. This access control level exists in languages like Java and C#, offering flexibility without exposing too much of a class’s internal workings.
Practical Examples in Apple SDKs:
layoutSubviews()
inUIView
(UIKit): This method is frequently overridden in subclasses but shouldn’t be called directly by users of those subclasses. If it were marked protected, it would prevent developers from misusing it outside of the context where it was intended to be overridden.draw(_:)
inUIView
andNSView
: Custom drawing logic is often provided by overridingdraw(_:)
, but it’s not meant to be called directly from outside the class. protected would allow subclass customization while preventing misuse by external code.touchesBegan(_:with:)
inUIResponder
(UIKit): Designed for subclassing to handle touch events, this method would benefit from being marked as protected. It’s part of the event handling chain, but it shouldn’t be called by external code directly.
Why Now?
As the Swift community has grown, so has our understanding of the balance between different programming paradigms. Protocols and structs are immensely powerful tools, but there are many areas—especially in Apple’s own frameworks—where classes are indispensable.
Given the evolution of Swift and the expanding use cases for Swift on different platforms (iOS, macOS, server-side Swift, etc.), now is the time to take another opportunity to enrich Swift’s class model by introducing these core object-oriented features: virtual, abstract, and protected. Provide explicitness, clarity, and control that are sorely needed for designing robust, flexible, and maintainable APIs.
Swift has always been about clarity and safety, and introducing these keywords would continue to push the language forward in those areas. We would retain all of the language’s current strengths while embracing the full power of classes in areas where they shine.
In Summary
virtual
: Gives developers an explicit way to mark methods that are intended to be overridden in subclasses, avoiding confusion and making inheritance more predictable.abstract
: Clearly defines classes and methods that cannot be instantiated or used directly, but must be implemented by subclasses.protected
: Offers better encapsulation, allowing methods and properties to be overridden or accessed by subclasses but not by external code.
This post aimed to present a comprehensive argument that connects Swift’s historical background with practical examples from Apple’s SDKs. Its main goal is to highlight the enduring relevance of classes in contemporary software development and propose improvements to Swift’s inheritance model by incorporating these features.
Thank you for taking the time to consider these ideas. I truly believe that virtual, abstract, and protected can enhance Swift’s class model while maintaining the language’s principles of clarity and safety. I’d love to hear your thoughts, feedback, and any additional ideas you might have on how we can improve Swift together.
Let’s continue to push the language forward, balancing the strengths of protocols, structs, and classes in the Swift ecosystem!
Warm regards,
-Van