# Swift Type Properties and Methods: static vs class Explained

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

Swift type properties and type methods let you attach data and behavior to the type itself instead of to individual instances. This matters whenever you need shared values, counters, factory-style helpers, configuration defaults, or functionality that belongs to a class, struct, or enum as a whole rather than to one object.

> **Quick answer:** Use `static` to create type properties and type methods that belong to the type itself. In classes, use `class` for type methods and computed type properties when you want subclasses to be able to override them; `static` cannot be overridden.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You'll understand this better if you know basic Swift syntax, how classes and structs are defined, and how instance properties and instance methods work.

## 1. What Is Swift Type Properties and Methods?

A type property or type method belongs to the type itself, not to any one instance created from that type.

- An **instance property** belongs to one specific object or value.
- A **type property** belongs to the class, struct, or enum itself.
- An **instance method** is called on an instance.
- A **type method** is called on the type name.
- Use `static` for type members on structs, enums, and classes.
- Use `class` only in classes, and only when you want an overridable type member.

For example, if every bank account should have its own balance, that is an instance property. But if the bank wants to track the total number of accounts created, that value belongs to the type, so a type property is a better fit.

> A common confusion is `static` vs `class`. Both create type-level members in classes, but only `class` supports overriding in subclasses.

## 2. Why Type Properties and Methods Matters

Type-level members help you model information and behavior that should be shared across all instances.

They are useful when:

- You need a shared constant such as a default tax rate or app-wide configuration value.
- You want to count how many instances have been created.
- You want helper behavior that does not depend on instance data.
- You need a factory-style method that creates instances in a standard way.
- You want subclasses to customize shared behavior using an overridable type method.

They are **not** the best choice when the data should be different for each instance. If each object needs its own value, use an instance property instead.

## 3. Basic Syntax

Start with the smallest useful examples. First, here is a type property and type method on a struct using `static`.

### Declaring a static type property

This property belongs to `GameSettings` itself, so you access it with the type name.

```swift
struct GameSettings {
    static let maxPlayers = 4
}

print(GameSettings.maxPlayers)
```

Output (tested with Swift 6.4):

```text
4
```

This prints `4`. No instance of `GameSettings` is needed.

### Declaring a static type method

This method also belongs to the type, not an instance.

```swift
struct MathHelper {
    static func square(_ number: Int) -> Int {
        return number * number
    }
}

let result = MathHelper.square(6)
print(result)
```

Output (tested with Swift 6.4):

```text
36
```

This prints `36`. Again, no instance is required.

### Using class for overridable type members

In classes, `class` can be used for type methods and computed type properties that subclasses may override.

```swift
class Vehicle {
    class var description: String {
        "Generic vehicle"
    }

    class func category() -> String {
        "Transport"
    }
}
```

This syntax is useful when subclasses should be allowed to provide a different implementation.

## 4. Step-by-Step Examples

### Example 1: Shared app configuration

Use a type property when a value should be shared everywhere.

```swift
struct AppConfig {
    static let apiBaseURL = "https://api.example.com"
}

print(AppConfig.apiBaseURL)
```

Output (tested with Swift 6.4):

```text
https://api.example.com
```

This keeps the configuration value in one place and avoids repeating the same string in many files.

### Example 2: Counting instances

A type property can track information shared by all instances.

```swift
class User {
    static var count = 0
    let name: String

    init(name: String) {
        self.name = name
        User.count += 1
    }
}

let user1 = User(name: "Ava")
let user2 = User(name: "Noah")

print(User.count)
```

Output (tested with Swift 6.4):

```text
2
```

This prints `2`. The counter is shared by all `User` instances.

### Example 3: A utility type method

Type methods are useful for logic that does not need instance data.

```swift
struct Temperature {
    static func celsiusToFahrenheit(_ celsius: Double) -> Double {
        return (celsius * 9 / 5) + 32
    }
}

let fahrenheit = Temperature.celsiusToFahrenheit(25)
print(fahrenheit)
```

Output (tested with Swift 6.4):

```text
77.0
```

This is a good fit because conversion does not depend on a stored instance.

### Example 4: Overridable class method

Use `class` when subclasses should customize type-level behavior.

```swift
class Notification {
    class func defaultSound() -> String {
        "default.wav"
    }
}

class UrgentNotification: Notification {
    override class func defaultSound() -> String {
        "alarm.wav"
    }
}

print(Notification.defaultSound())
print(UrgentNotification.defaultSound())
```

Output (tested with Swift 6.4):

```text
default.wav
alarm.wav
```

The base class and subclass can now provide different type-level behavior.

## 5. Practical Use Cases

- Defining shared constants such as API endpoints, app version strings, or default limits.
- Tracking global counters, such as the number of created objects or active sessions.
- Creating helper functions like formatters, converters, validators, or factory methods.
- Providing default behavior for a class hierarchy that subclasses can override with `class` methods.
- Grouping logic under a type name so related functionality is easier to discover and maintain.
- Storing cached values that should be shared by all instances of a type.

## 6. Common Mistakes

### Mistake 1: Accessing a type property through an instance

Beginners often try to read a type property from an instance because instance properties are accessed that way. Type properties must be accessed with the type name.

**Problem:** This code tries to access a shared type property through an instance, which causes a compile-time error because the member belongs to the type, not the object.

```swift
struct Settings {
    static let theme = "Dark"
}

let settings = Settings()
print(settings.theme)
```

**Fix:** Access type members using the type name itself.

```swift
struct Settings {
    static let theme = "Dark"
}

print(Settings.theme)
```

Output (tested with Swift 6.4):

```text
Dark
```

The corrected version works because `theme` is owned by `Settings`, not by a specific instance.

### Mistake 2: Using static when you need overriding

In classes, developers sometimes write a `static` method and later try to override it in a subclass. That is not allowed.

**Problem:** A `static` type method in a class is final-like for overriding purposes, so attempting to override it produces a compiler error such as `Cannot override static method`.

```swift
class Animal {
    static func sound() -> String {
        "Some sound"
    }
}

class Dog: Animal {
    override class func sound() -> String {
        "Bark"
    }
}
```

**Fix:** Use `class` for the base type method if subclasses should override it.

```swift
class Animal {
    class func sound() -> String {
        "Some sound"
    }
}

class Dog: Animal {
    override class func sound() -> String {
        "Bark"
    }
}
```

The corrected version works because `class` methods in classes are overridable.

### Mistake 3: Expecting each instance to have its own static value

A `static` property is shared across all instances. If you change it, every instance sees the same updated value.

**Problem:** This code uses a type property for data that should be unique per object, so changing one value affects all instances and produces incorrect behavior.

```swift
class Player {
    static var score = 0
}

Player.score = 10
print(Player.score)
```

Output (tested with Swift 6.4):

```text
10
```

**Fix:** Use an instance property when each object needs its own value.

```swift
class Player {
    var score = 0
}

let player1 = Player()
let player2 = Player()

player1.score = 10
print(player1.score)
print(player2.score)
```

Output (tested with Swift 6.4):

```text
10
0
```

The corrected version works because each `Player` instance now stores its own score independently.

### Mistake 4: Trying to make a stored class property

Swift allows stored type properties with `static`, but `class` is used for overridable computed type properties, not stored ones.

**Problem:** This code attempts to declare a stored type property with `class`, which Swift does not allow.

```swift
class Account {
    class var minimumBalance = 100
}
```

**Fix:** Use `static` for stored type properties, or use a computed `class` property when overriding is needed.

```swift
class Account {
    static var minimumBalance = 100
}

class PremiumAccount {
    class var minimumBalance: Int {
        500
    }
}
```

The corrected version uses the right feature for each case: stored shared data with `static`, overridable computed behavior with `class`.

## 7. Best Practices

### Use type members only for truly shared data

If a value should be the same for every instance, a type property is a clean choice. If different objects need different values, keep it as an instance property.

```swift
struct Theme {
    static let primaryColor = "Blue"
}
```

This works well because the primary color is shared application-wide.

### Prefer let for shared constants that should not change

If a type-level value is meant to stay fixed, declare it with `let`. This makes intent clear and prevents accidental mutation.

```swift
struct Limits {
    static let maxUploadSize = 10
}
```

This is safer than using a mutable property when the value should remain constant.

### Choose class only when subclasses must customize behavior

In classes, prefer `static` by default unless there is a real need for overriding. Use `class` only when the design explicitly requires subclass customization.

```swift
class Report {
    class func fileExtension() -> String {
        "txt"
    }
}
```

This choice communicates that subclasses may provide a different file extension.

### Keep utility methods stateless when possible

A type method is easiest to understand when it does not rely on hidden shared mutable state.

```swift
struct Validator {
    static func isAdult(age: Int) -> Bool {
        age >= 18
    }
}
```

This makes the method easy to test and predictable to use.

## 8. Limitations and Edge Cases

- `static` members on classes cannot be overridden by subclasses.
- `class` is only available on classes, not structs or enums.
- Stored type properties use `static`; a stored `class` property is not allowed.
- Shared mutable type properties can make code harder to reason about because changes affect all instances.
- If a type property seems to be “not working,” check whether you actually needed an instance property instead.
- Type methods cannot directly use instance properties unless you first create or receive an instance.
- Type properties are lazily initialized in Swift, which is useful for setup but can surprise beginners who expect immediate initialization at program start.

## 9. Practical Mini Project

This mini project builds a simple employee system that uses type properties and methods in realistic ways. It tracks the total number of employees, stores a shared company name, and provides an overridable class method for employee role labels.

```swift
class Employee {
    static let companyName = "Northwind Labs"
    static var totalEmployees = 0

    let name: String

    init(name: String) {
        self.name = name
        Employee.totalEmployees += 1
    }

    class func roleLabel() -> String {
        "Employee"
    }

    func summary() -> String {
        "\(name) works at \(Employee.companyName) as a \(type(of: self).roleLabel())."
    }
}

class Manager: Employee {
    override class func roleLabel() -> String {
        "Manager"
    }
}

let employee1 = Employee(name: "Lena")
let employee2 = Manager(name: "Marcus")

print(employee1.summary())
print(employee2.summary())
print("Total employees: \(Employee.totalEmployees)")
```

Output (tested with Swift 6.4):

```text
Lena works at Northwind Labs as a Employee.
Marcus works at Northwind Labs as a Manager.
Total employees: 2
```

This example combines several important ideas: shared constants with `static let`, shared mutable counters with `static var`, and subclass customization with an overridable `class` method.

## 10. Key Points

- Type properties and methods belong to the type itself, not to individual instances.
- Use `static` for type-level members on structs, enums, and classes.
- Use `class` only in classes when subclasses should be able to override the member.
- Access type members with the type name, such as `User.count`.
- Use type properties for shared data and instance properties for per-object data.
- Stored type properties use `static`, while overridable type properties with `class` are computed.

## 11. Practice Exercise

Create a `Book` class with:

- A shared company-wide library name stored as a type property.
- A shared counter that tracks how many books have been created.
- An instance property for the book title.
- A type method that returns a category label.
- A subclass named `ReferenceBook` that overrides the category label.

**Expected output:** Two book summaries and the total number of books created.

**Hint:** Use `static` for shared stored values and `class func` for the overridable type method.

```swift
class Book {
    static let libraryName = "City Library"
    static var bookCount = 0

    let title: String

    init(title: String) {
        self.title = title
        Book.bookCount += 1
    }

    class func category() -> String {
        "General"
    }

    func summary() -> String {
        "\(title) is a \(type(of: self).category()) book in \(Book.libraryName)."
    }
}

class ReferenceBook: Book {
    override class func category() -> String {
        "Reference"
    }
}

let book1 = Book(title: "Swift Basics")
let book2 = ReferenceBook(title: "Swift Standard Library Guide")

print(book1.summary())
print(book2.summary())
print("Total books: \(Book.bookCount)")
```

Output (tested with Swift 6.4):

```text
Swift Basics is a General book in City Library.
Swift Standard Library Guide is a Reference book in City Library.
Total books: 2
```

## 12. Final Summary

Swift type properties and type methods are the right tool when data or behavior belongs to the type as a whole instead of to a single instance. Use `static` for shared values and helper behavior, and remember that those members are accessed through the type name, not an instance.

When working with classes, the key decision is usually `static` vs `class`. Choose `static` when overriding is not needed, and choose `class` when subclasses should provide their own implementation of a type method or computed type property.

A good next step is to study Swift instance methods, inheritance, and computed properties together with this topic. Those concepts make it much easier to decide whether behavior belongs to an instance or to the type itself.
