Swift
programming
class method
subclass
method overriding

Swift - class method which must be overridden by subclass

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Swift does not have an abstract keyword for classes or methods, so there is no built-in way to declare a class method that the compiler forces every subclass to override. If you need that kind of contract, you have to model it indirectly.

In practice, there are three common approaches: a base-class method that traps at runtime, a protocol that enforces the requirement at compile time, or a redesign that uses instance methods and template-method hooks instead of a required class method.

Use class, Not static, When Overrides Are Intended

In Swift, static methods cannot be overridden in subclasses, while class methods can:

swift
1class Parent {
2    class func canOverride() {}
3    static func cannotOverride() {}
4}
5
6class Child: Parent {
7    override class func canOverride() {}
8    // override static func cannotOverride() {}  // invalid
9}

So if subclass customization is even possible, the base declaration has to use class.

Runtime-Enforced Override With fatalError

A common inheritance-based pattern is to define the method in the base class and make the default implementation crash intentionally:

swift
1import Foundation
2
3class ReportFormatter {
4    class func format(_ input: String) -> String {
5        fatalError("Subclasses must override format")
6    }
7}
8
9final class HTMLFormatter: ReportFormatter {
10    override class func format(_ input: String) -> String {
11        "<p>\(input)</p>"
12    }
13}
14
15print(HTMLFormatter.format("hello"))

This is simple and explicit, but the enforcement happens at runtime rather than at compile time. If a subclass forgets to override, you only find out when that method is actually called.

Prefer Protocols for Compile-Time Enforcement

If compile-time enforcement matters more than base-class inheritance, a protocol is often the better design:

swift
1import Foundation
2
3protocol FormattableType {
4    static func format(_ input: String) -> String
5}
6
7struct JSONFormatter: FormattableType {
8    static func format(_ input: String) -> String {
9        #"{"value":""# + input + #""}"#
10    }
11}
12
13print(JSONFormatter.format("hello"))

Any type that claims to conform to FormattableType must implement the required method or the code will not compile.

This is usually a cleaner contract than a runtime trap if you do not strictly need class inheritance.

Combine a Base Class With a Protocol When Needed

Sometimes you want shared class behavior and a compile-time contract. In that case, combine both:

swift
1import Foundation
2
3protocol EndpointBuilder {
4    static func basePath() -> String
5}
6
7class APIResource {
8    class func timeoutSeconds() -> Int { 30 }
9}
10
11final class UserResource: APIResource, EndpointBuilder {
12    static func basePath() -> String { "/users" }
13}
14
15print(UserResource.timeoutSeconds())
16print(UserResource.basePath())

The base class provides shared defaults, while the protocol forces each conforming subtype to provide the required class-level behavior.

Consider the Template Method Alternative

If the real requirement is not "subclasses must override this class method" but rather "subclasses must provide one part of an algorithm," an instance-level template method is often a better design:

swift
1import Foundation
2
3class JobRunner {
4    final func run() {
5        preRun()
6        execute()
7        postRun()
8    }
9
10    func preRun() {}
11    func execute() { fatalError("Subclasses must override execute") }
12    func postRun() {}
13}
14
15final class ImportRunner: JobRunner {
16    override func execute() {
17        print("Import logic")
18    }
19}

This often fits dependency injection and instance state better than forcing a class method into the design.

Common Pitfalls

The biggest mistake is using static in the base class and later expecting subclasses to override it. That simply is not how Swift's override model works.

Another common issue is relying on fatalError without documenting the contract clearly. A runtime trap is only useful if the team knows why it is there.

People also reach for inheritance when a protocol would express the requirement more clearly and with compile-time enforcement.

Finally, not every required extension point needs to be class-level. Sometimes the cleanest solution is to move the customization to an instance method.

Summary

  • Swift does not have abstract class methods built into the language.
  • Use class methods when override capability is required; static methods cannot be overridden.
  • A base implementation with fatalError enforces the requirement at runtime.
  • A protocol gives compile-time enforcement and is often the cleaner choice.
  • Reconsider whether the requirement should really be an instance-level extension point instead.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.