# Swift Method Overriding: How Subclasses Replace Behavior

Source: https://devdocs10.com/articles/swift/swift-method-overriding  
By Murat Bo · Published 2026-05-25 · DevDocs10 (https://devdocs10.com)

Swift method overriding lets a subclass provide its own implementation of a method that it inherits from a superclass. This is a core part of inheritance, and it matters whenever you want related classes to share a common design while still behaving differently in specific situations.

> **Quick answer:** In Swift, a subclass overrides an inherited method by writing a method with the same name and signature and adding the `override` keyword. You can also call the superclass version with `super` when you want to extend rather than completely replace the original behavior.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You will understand this better if you already know basic Swift class syntax, how inheritance works, and how functions are declared with parameters and return types.

## 1. What Is Method Overriding?

Method overriding happens when a subclass replaces an inherited method with its own version. The new version must match the inherited method's name, parameter list, and return type, and it must be marked with `override`.

- A superclass defines behavior that child classes inherit.
- A subclass can replace inherited behavior by overriding a method.
- Swift requires the `override` keyword so the compiler can verify your intent.
- You can call the original superclass implementation with `super`.
- Only methods that are inherited can be overridden.

For example, if a base class defines a general `speak()` method, different subclasses can override it to provide their own version. This lets you write general code around the base class while still getting specific behavior from each subclass.

> Method overriding is different from method overloading. Overriding replaces inherited behavior in a subclass. Overloading means creating multiple methods with the same name but different parameter lists.

## 2. Why Method Overriding Matters

Overriding matters because it makes inheritance useful in real programs. Without overriding, every subclass would be forced to use the exact same inherited behavior, even when that behavior does not fit.

Here are common reasons to use it:

- To specialize shared behavior for a specific subclass.
- To keep a common interface while changing implementation details.
- To let polymorphic code work with many subclasses through one base type.
- To extend base behavior by calling `super` and then adding more work.

Imagine a game with a base `Enemy` class. Every enemy may have an `attack()` method, but each enemy type attacks differently. Overriding gives each subclass its own attack behavior while preserving the same method name.

You should use method overriding when the subclass truly represents a more specific version of the superclass. If two types are not in a clear inheritance relationship, overriding is usually the wrong tool.

## 3. Basic Syntax

The basic pattern is: create a superclass method, inherit from that class, and write a matching method in the subclass with the `override` keyword.

### Defining the superclass method

This class provides a method that can be inherited by subclasses.

```swift
class Animal {
    func speak() {
        print("Some generic animal sound")
    }
}
```

The `Animal` class defines a normal instance method named `speak()`.

### Overriding in the subclass

This subclass inherits from `Animal` and replaces the inherited method.

```swift
class Dog: Animal {
    override func speak() {
        print("Woof!")
    }
}
```

The `override` keyword tells Swift that `Dog` is intentionally replacing an inherited method. If there is no matching method in the superclass, Swift reports an error.

### Using the overridden method

When you call the method on an instance of the subclass, Swift uses the subclass implementation.

```swift
let dog = Dog()
dog.speak()
```

Output (tested with Swift 6.4):

```text
Woof!
```

This prints `Woof!` because the subclass version overrides the superclass version.

## 4. Step-by-Step Examples

### Example 1: Basic override

This example shows the simplest use of overriding.

```swift
class Vehicle {
    func description() {
        print("A general vehicle")
    }
}

class Car: Vehicle {
    override func description() {
        print("A car with four wheels")
    }
}

let car = Car()
car.description()
```

Output (tested with Swift 6.4):

```text
A car with four wheels
```

The subclass keeps the same method name but changes the behavior. This is the most common form of overriding.

### Example 2: Calling the superclass method with super

Sometimes you do not want to fully replace the original behavior. You want to keep it and then add more work.

```swift
class Employee {
    func work() {
        print("Completing general tasks")
    }
}

class Manager: Employee {
    override func work() {
        super.work()
        print("Reviewing team progress")
    }
}

let manager = Manager()
manager.work()
```

Output (tested with Swift 6.4):

```text
Completing general tasks
Reviewing team progress
```

This prints both messages. Calling `super.work()` runs the superclass implementation first, then the subclass adds its own behavior.

### Example 3: Overriding a method with parameters

The overridden method must match the full method signature, including parameters.

```swift
class Shape {
    func draw(using color: String) {
        print("Drawing a shape in \(color)")
    }
}

class Circle: Shape {
    override func draw(using color: String) {
        print("Drawing a circle in \(color)")
    }
}

let circle = Circle()
circle.draw(using: "blue")
```

Output (tested with Swift 6.4):

```text
Drawing a circle in blue
```

If the parameter labels or types do not match, Swift treats it as a different method instead of a valid override.

### Example 4: Polymorphism through a superclass reference

One of the biggest benefits of overriding is that subclass behavior still works even when the variable type is the superclass.

```swift
class NotificationSender {
    func send() {
        print("Sending a generic notification")
    }
}

class EmailSender: NotificationSender {
    override func send() {
        print("Sending an email notification")
    }
}

class TextMessageSender: NotificationSender {
    override func send() {
        print("Sending a text message notification")
    }
}

let senders: [NotificationSender] = [EmailSender(), TextMessageSender()]

for sender in senders {
    sender.send()
}
```

Output (tested with Swift 6.4):

```text
Sending an email notification
Sending a text message notification
```

Even though the array type is `NotificationSender`, Swift calls the correct overridden method for each actual subclass instance.

## 5. Practical Use Cases

Method overriding is especially useful in class hierarchies where related types share a common contract but need different implementations.

- Building UI component classes where child components draw or respond differently.
- Creating game entities such as enemies, players, and bosses with customized actions.
- Modeling business roles like `Employee`, `Manager`, and `Director` with specialized behavior.
- Defining base networking or logging classes and customizing request handling or formatting.
- Writing reusable framework code where subclasses adapt a shared lifecycle method.

In each case, the parent class defines a common interface and the subclasses provide behavior that matches their role.

## 6. Common Mistakes

### Mistake 1: Forgetting the override keyword

Swift requires the `override` keyword whenever a subclass replaces an inherited method. Beginners often write a matching method but forget to mark it.

**Problem:** Without `override`, Swift cannot confirm that you intended to replace an inherited method, so the code fails to compile.

```swift
class Animal {
    func move() {
        print("Animal moves")
    }
}

class Bird: Animal {
    func move() {
        print("Bird flies")
    }
}
```

**Fix:** Add `override` before the method declaration.

```swift
class Animal {
    func move() {
        print("Animal moves")
    }
}

class Bird: Animal {
    override func move() {
        print("Bird flies")
    }
}
```

The corrected version works because Swift now knows that the subclass is intentionally overriding the inherited method.

### Mistake 2: Signature does not match the superclass method

An override must match the inherited method exactly. Small differences in parameter labels, parameter types, or return type break the override.

**Problem:** This code does not match the superclass method signature, so Swift reports an error such as `method does not override any method from its superclass`.

```swift
class Printer {
    func printPage(count: Int) {
        print("Printing \(count) pages")
    }
}

class OfficePrinter: Printer {
    override func printPage(total: Int) {
        print("Office printer: \(total) pages")
    }
}
```

**Fix:** Use the exact same method signature as the superclass.

```swift
class Printer {
    func printPage(count: Int) {
        print("Printing \(count) pages")
    }
}

class OfficePrinter: Printer {
    override func printPage(count: Int) {
        print("Office printer: \(count) pages")
    }
}
```

The corrected version works because the subclass method now matches the inherited method exactly.

### Mistake 3: Forgetting to call super when base behavior is still needed

Sometimes a subclass should add behavior rather than replace everything. If you forget to call `super`, you may accidentally skip important superclass logic.

**Problem:** This override replaces the superclass behavior completely, so setup or shared work in the parent class never runs.

```swift
class Document {
    func save() {
        print("Saving document to disk")
    }
}

class EncryptedDocument: Document {
    override func save() {
        print("Encrypting document")
    }
}
```

**Fix:** Call `super.save()` if the superclass work must still happen.

```swift
class Document {
    func save() {
        print("Saving document to disk")
    }
}

class EncryptedDocument: Document {
    override func save() {
        print("Encrypting document")
        super.save()
    }
}
```

The corrected version works because it preserves the inherited save behavior while adding encryption logic.

### Mistake 4: Trying to override a static method

In Swift, `static` methods on classes are not overridable. Beginners sometimes expect all class-level methods to support overriding.

**Problem:** A `static` method belongs to the class in a way that does not allow subclass overrides, so the compiler rejects this pattern.

```swift
class BaseLogger {
    static func category() -> String {
        "Base"
    }
}

class FileLogger: BaseLogger {
    override static func category() -> String {
        "File"
    }
}
```

**Fix:** Use `class` instead of `static` on the superclass method if you want subclasses to override it.

```swift
class BaseLogger {
    class func category() -> String {
        "Base"
    }
}

class FileLogger: BaseLogger {
    override class func category() -> String {
        "File"
    }
}
```

The corrected version works because `class func` on classes can be overridden, while `static func` cannot.

## 7. Best Practices

### Practice 1: Override only when the subclass relationship is real

Overriding is most useful when the subclass is genuinely a specialized form of the superclass. If the relationship is weak, inheritance can make code harder to maintain.

A less helpful design might force unrelated types into a class hierarchy:

```swift
class Report {
    func export() {
        print("Exporting report")
    }
}
```

A better design uses overriding only when child types are truly specialized versions of the base type:

```swift
class Report {
    func export() {
        print("Exporting a generic report")
    }
}

class PDFReport: Report {
    override func export() {
        print("Exporting report as PDF")
    }
}
```

This keeps inheritance meaningful and makes the override easier to understand.

### Practice 2: Call super when shared behavior should remain

If the parent method performs important shared work, keep it by calling `super`. Replace the parent completely only when that is the intended design.

```swift
class Session {
    func start() {
        print("Opening session")
    }
}

class SecureSession: Session {
    override func start() {
        super.start()
        print("Performing security checks")
    }
}
```

This approach avoids silently losing important logic that exists in the superclass.

### Practice 3: Keep overridden methods consistent with the original meaning

An override should still match the purpose of the original method. If the base method means one thing and the subclass does something unrelated, the hierarchy becomes confusing.

```swift
class Payment {
    func process() {
        print("Processing payment")
    }
}

class CreditCardPayment: Payment {
    override func process() {
        print("Charging the credit card")
    }
}
```

This is a good override because the subclass still performs the same conceptual action: processing a payment.

## 8. Limitations and Edge Cases

- You can only override methods inherited from a superclass. Protocol methods are implemented, not overridden, unless a superclass already provides them.
- `static` class-level methods cannot be overridden. Use `class func` when overridable class methods are needed.
- The method signature must match exactly, including external parameter labels and return type.
- Overriding applies to classes, not structs or enums, because structs and enums do not support inheritance.
- If a method in the superclass is marked in a way that prevents overriding, the subclass cannot replace it.
- A method may appear to be "not overriding" when the names match but the labels differ, which is a common Swift-specific source of confusion.
- Overuse of overriding can create class hierarchies that are difficult to reason about. Sometimes composition is a better design choice.

## 9. Practical Mini Project

Here is a small complete example that uses method overriding to model different delivery methods. The base class provides shared structure, and each subclass overrides the delivery action.

```swift
class Message {
    let recipient: String

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

    func send() {
        print("Sending a generic message to \(recipient)")
    }
}

class EmailMessage: Message {
    override func send() {
        print("Sending email to \(recipient)")
    }
}

class SMSMessage: Message {
    override func send() {
        print("Sending SMS to \(recipient)")
    }
}

let messages: [Message] = [
    EmailMessage(recipient: "alex@example.com"),
    SMSMessage(recipient: "555-1234")
]

for message in messages {
    message.send()
}
```

Output (tested with Swift 6.4):

```text
Sending email to alex@example.com
Sending SMS to 555-1234
```

This example shows the main benefit of overriding: the loop works with the shared base type `Message`, but each instance still performs its own specialized action.

## 10. Key Points

- Method overriding lets a subclass replace an inherited method with its own implementation.
- Swift requires the `override` keyword for valid overrides.
- You can call `super` to keep superclass behavior and add more logic.
- The method signature must match exactly for an override to work.
- `static` methods cannot be overridden, but `class func` can be.
- Overriding is one of the main tools that makes polymorphism possible in class hierarchies.

## 11. Practice Exercise

Build a small class hierarchy for notifications.

- Create a superclass named `Alert` with a method `show()` that prints a generic message.
- Create a subclass named `SuccessAlert` that overrides `show()` and prints a success message.
- Create another subclass named `ErrorAlert` that overrides `show()` and prints an error message.
- Store both subclasses in an array of type `[Alert]` and call `show()` in a loop.

**Expected output:** One success message and one error message, each coming from the correct subclass implementation.

**Hint:** Make sure each subclass method uses the `override` keyword and has exactly the same signature as the superclass method.

```swift
class Alert {
    func show() {
        print("Showing a generic alert")
    }
}

class SuccessAlert: Alert {
    override func show() {
        print("Showing a success alert")
    }
}

class ErrorAlert: Alert {
    override func show() {
        print("Showing an error alert")
    }
}

let alerts: [Alert] = [SuccessAlert(), ErrorAlert()]

for alert in alerts {
    alert.show()
}
```

Output (tested with Swift 6.4):

```text
Showing a success alert
Showing an error alert
```

## 12. Final Summary

Swift method overriding allows subclasses to replace inherited behavior while keeping a shared class design. To override a method correctly, the subclass must inherit from a superclass, use the same method signature, and include the `override` keyword. When needed, the subclass can also call `super` to preserve the original implementation and extend it.

This feature is important because it supports polymorphism: you can work with a base class type while still getting subclass-specific behavior at runtime. As you continue learning Swift classes, a good next step is to study property overriding, initializers in inheritance, and the difference between inheritance and protocol-based design.
