# Swift Inheritance: Subclassing Classes and Overriding Methods

Source: https://devdocs10.com/articles/swift/swift-inheritance  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

Swift inheritance lets one class build on the behavior of another class. It is the core feature that makes class hierarchies possible, so you can reuse code, specialize behavior, and model “is-a” relationships in a clean way.

> **Quick answer:** In Swift, inheritance is available only for classes, not structs or enums. A subclass inherits stored and computed properties, methods, and subscripts from its superclass, and can customize them with `override` or add new behavior of its own.

> **Difficulty:** Beginner
>
> **You'll understand this better if you know:** basic Swift types, how functions and properties work, and the difference between classes and structs.

## 1. What Is Swift Inheritance?

Inheritance is a relationship between two classes where a new class, called a subclass, starts with the behavior of an existing class, called a superclass. The subclass can keep what it inherits, add more properties or methods, and replace some behavior when needed.

- It works only with `class` types.
- A subclass automatically gets inherited members from its superclass.
- You can customize inherited behavior using `override`.
- Inheritance is useful when one type is clearly a specialized version of another.

For example, a `Car` class might inherit from a more general `Vehicle` class, because every car is a vehicle, but not every vehicle is a car.

## 2. Why Swift Inheritance Matters

Inheritance helps you avoid repeating shared code across related classes. It also gives you a structured way to extend behavior while keeping common logic in one place.

In real code, inheritance is useful when several types share the same base state or rules, but each type needs its own details. It is especially helpful for object-oriented models, framework subclasses, and type hierarchies where shared behavior should live in a common base class.

Inheritance is not the default way to design every Swift program. In many cases, composition is a better fit, but inheritance remains important when a true superclass/subclass relationship exists and polymorphic behavior is needed.

## 3. Basic Syntax

A subclass is declared with a colon after the class name. The subclass name comes first, then the superclass name.

### Minimal subclass syntax

This example shows the basic shape of a class hierarchy and one overridden method.

```swift
class Vehicle {
    var speed : Int = 0
    
    func describe() -> String {
        return "Moving at \(speed) km/h"
    }
}

class Car : Vehicle {
    var gearCount : Int = 6
    
    override func describe() -> String {
        return "Car moving at \(speed) km/h with \(gearCount) gears"
    }
}
```

Here, `Car` inherits `speed` and `describe()` from `Vehicle`, then adds `gearCount` and overrides the method to provide more specific output.

## 4. Step-by-Step Examples

### Example 1: Inheriting stored properties

A subclass automatically receives the superclass’s stored properties, so you can use them immediately in the child type.

```swift
class Animal {
    var name : String
    
    init(name: String) {
        self.name = name
    }
}

class Dog : Animal {
    func speak() -> String {
        return "\(name) says woof"
    }
}

let pet = Dog(name: "Milo")
print(pet.speak())
```

Output (tested with Swift 6.4):

```text
Milo says woof
```

The subclass uses the inherited `name` property without redefining it. Because the superclass initializer sets that property, the subclass can focus on its own behavior.

### Example 2: Overriding a method

When inherited behavior is close, but not quite right, override the method and use `super` if you want to keep part of the original implementation.

```swift
class Appliance {
    func status() -> String {
        return "Appliance is on"
    }
}

class Washer : Appliance {
    override func status() -> String {
        return super.status() + " and washing clothes"
    }
}
```

This pattern is useful when the subclass wants to refine the superclass result instead of replacing it completely.

### Example 3: Adding new properties and methods

Inheritance is not only about reuse. A subclass can also introduce new state and behavior that belongs only to the more specific type.

```swift
class Employee {
    var name : String
    
    init(name: String) {
        self.name = name
    }
}

class Manager : Employee {
    var teamSize : Int
    
    init(name: String, teamSize: Int) {
        self.teamSize = teamSize
        super.init(name: name)
    }
    
    func report() -> String {
        return "\(name) manages \(teamSize) people"
    }
}
```

The subclass stores its own data while still relying on the inherited `name` property from `Employee`.

### Example 4: Overriding a computed property

Inherited computed properties can also be replaced if the subclass needs a different result.

```swift
class Shape {
    var description: String {
        return "A shape"
    }
}

class Circle : Shape {
    override var description: String {
        return "A circle"
    }
}
```

This is a common pattern when a base type provides a general description and the subclass needs a more precise one.

## 5. Practical Use Cases

- Modeling a domain hierarchy such as `Vehicle` → `ElectricCar` or `Document` → `Invoice`.
- Sharing common validation or formatting logic across closely related objects.
- Customizing framework base classes where a superclass defines hooks meant to be overridden.
- Building reusable components that need a common baseline but different specializations.
- Representing polymorphic behavior where code accepts a superclass reference but works with many subclass instances.

In Swift, inheritance is most appropriate when shared identity and behavior are real, not just convenient. If types are not truly in an is-a relationship, composition is often the better design.

## 6. Common Mistakes

### Mistake 1: Trying to inherit from a struct or enum

Only classes support inheritance in Swift. Beginners often try to use a `struct` as a base type because it feels lighter than a class.

**Problem:** A struct cannot be a superclass, so Swift reports an error such as “inheritance from non-protocol type” or a similar compiler message depending on the declaration.

```swift
struct Animal {
    var name: String
}

class Dog : Animal {
}
```

**Fix:** Make the base type a class if you need inheritance. Unlike a struct, a class gets no memberwise initializer, so it needs an `init` that sets `name`.

```swift
class Animal {
    var name: String

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

class Dog : Animal {
}
```

The corrected version works because classes are the only nominal types that can participate in inheritance.

### Mistake 2: Forgetting `override` on a replaced member

When a subclass intentionally replaces inherited behavior, Swift requires the `override` keyword. This protects you from accidentally shadowing a superclass member.

**Problem:** Without `override`, Swift emits an error like “overriding declaration requires an override keyword.”

```swift
class Bird {
    func sound() -> String {
        return "chirp"
    }
}

class Parrot : Bird {
    func sound() -> String {
        return "hello"
    }
}
```

**Fix:** Add `override` to make the intent explicit.

```swift
class Bird {
    func sound() -> String {
        return "chirp"
    }
}

class Parrot : Bird {
    override func sound() -> String {
        return "hello"
    }
}
```

The fixed version works because Swift now knows the subclass is intentionally replacing the inherited method.

### Mistake 3: Calling `super.init` too late or not at all

Subclass initializers must fully initialize subclass properties before calling a superclass initializer, and they must eventually initialize the inherited state correctly.

**Problem:** If you call `super.init` before setting all subclass stored properties, Swift rejects the initializer because the object is not fully initialized yet.

```swift
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

class Teacher : Person {
    var subject: String
    
    init(name: String, subject: String) {
        super.init(name: name)
        self.subject = subject
    }
}
```

**Fix:** Initialize subclass properties first, then call `super.init`.

```swift
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

class Teacher : Person {
    var subject: String
    
    init(name: String, subject: String) {
        self.subject = subject
        super.init(name: name)
    }
}
```

The corrected initializer works because Swift’s two-phase initialization rules are satisfied.

## 7. Best Practices

### Practice 1: Use inheritance only for a real is-a relationship

Inheritance should represent genuine specialization, not just code reuse. If the subclass is not really a more specific version of the superclass, the hierarchy becomes confusing.

```swift
// Better design: a Car is a Vehicle
class Vehicle { }
class Car : Vehicle { }
```

This keeps the hierarchy meaningful and makes future maintenance easier.

### Practice 2: Mark classes `final` when you do not want subclassing

If a class is not designed as a base class, declare it `final`. This prevents accidental inheritance and can make intent clearer.

```swift
final class SessionCache {
    func clear() {
        // implementation
    }
}
```

Using `final` avoids unnecessary inheritance and documents that the class is meant to stand on its own.

### Practice 3: Keep superclass APIs small and focused

A superclass with too many responsibilities becomes hard to subclass safely. A smaller base API makes overriding easier and reduces the chance of fragile behavior.

```swift
class Report {
    func title() -> String {
        return "Monthly Report"
    }
}
```

Smaller superclass contracts are easier to understand and safer to extend in subclasses.

## 8. Limitations and Edge Cases

- Inheritance works only for classes; `struct` and `enum` types cannot inherit from one another.
- Swift supports single inheritance for classes, so a class can inherit from only one superclass.
- Stored properties in a superclass are inherited, but you cannot directly override stored properties with another stored property.
- Computed properties, methods, subscripts, and some class members can be overridden when marked appropriately.
- Subclass initializers must follow Swift’s initialization rules, which can feel strict compared with some other languages.
- A subclass can hide or replace inherited behavior unexpectedly if the superclass was not designed for extension.
- Attempting to override a non-overrideable member produces compile-time errors, such as missing or invalid `override` usage.

One common surprise is that inheritance is not a substitute for every kind of shared code. Many Swift APIs prefer protocols and composition when the goal is shared capability rather than shared state.

## 9. Practical Mini Project

Let’s build a tiny inventory model with a base `Product` class and two subclasses. The goal is to show a complete class hierarchy with inheritance, overriding, and superclass initializers.

```swift
class Product {
    let name: String
    let price: Double
    
    init(name: String, price: Double) {
        self.name = name
        self.price = price
    }
    
    func summary() -> String {
        return "\(name) costs $\(price)"
    }
}

class Book : Product {
    let author: String
    
    init(name: String, price: Double, author: String) {
        self.author = author
        super.init(name: name, price: price)
    }
    
    override func summary() -> String {
        return super.summary() + " by \(author)"
    }
}

class DigitalProduct : Product {
    let downloadSizeMB: Int
    
    init(name: String, price: Double, downloadSizeMB: Int) {
        self.downloadSizeMB = downloadSizeMB
        super.init(name: name, price: price)
    }
}

let novel = Book(name: "Swift Patterns", price: 29.99, author: "A. Developer")
let app = DigitalProduct(name: "Photo Editor", price: 49.0, downloadSizeMB: 180)

print(novel.summary())
print(app.summary())
```

Output (tested with Swift 6.4):

```text
Swift Patterns costs $29.99 by A. Developer
Photo Editor costs $49.0
```

This example shows how a shared superclass can hold common data and behavior while subclasses add details that are specific to each product type. It also demonstrates the use of `super.init` and `super.summary()` in a realistic way.

## 10. Key Points

- Swift inheritance lets one class reuse and customize the behavior of another class.
- Only classes can inherit in Swift; structs and enums cannot.
- Use `override` whenever a subclass replaces inherited behavior.
- Use `super` to access superclass implementations and initializers.
- Inheritance is best for real is-a relationships, not just code sharing.
- Mark classes `final` when subclassing should be prevented.

## 11. Practice Exercise

- Create a base class named `Appliance` with a `brand` property and a `details()` method.
- Create a subclass named `Microwave` with an additional `powerWatts` property.
- Override `details()` so it includes both the brand and the wattage.
- Instantiate the subclass and print the result.

**Expected output:** A string that includes the appliance brand and its power rating.

**Hint:** Remember to initialize subclass properties before calling `super.init`, and use `override` on the method.

```swift
class Appliance {
    let brand: String
    
    init(brand: String) {
        self.brand = brand
    }
    
    func details() -> String {
        return "Brand: \(brand)"
    }
}

class Microwave : Appliance {
    let powerWatts: Int
    
    init(brand: String, powerWatts: Int) {
        self.powerWatts = powerWatts
        super.init(brand: brand)
    }
    
    override func details() -> String {
        return super.details() + ", Power: \(powerWatts)W"
    }
}

let oven = Microwave(brand: "KitchenPro", powerWatts: 900)
print(oven.details())
```

Output (tested with Swift 6.4):

```text
Brand: KitchenPro, Power: 900W
```

## 12. Final Summary

Swift inheritance gives classes a way to share and extend behavior through a superclass and subclass relationship. It is a powerful tool for modeling specialized types, reducing repeated code, and customizing inherited methods or properties with `override`.

At the same time, inheritance comes with rules: only classes can participate, only one superclass is allowed, and initializers must follow Swift’s strict initialization model. Those rules help keep class hierarchies predictable and safe.

If you are learning Swift inheritance, a good next step is to study `override`, `super`, designated and convenience initializers, and when to choose composition instead of subclassing.
