iosNudge

What is the difference between static and class members in Swift?

EasySwift 2 min read

TL;DR

static and class both create members that belong to the type, not to an instance. static cannot be overridden. It works in structs, enums, classes, actors and protocols. class can be overridden by subclasses. It works only in classes, and only for methods and computed properties.

Type members in general

struct MathUtils {
    static let pi = 3.14159
    static func square(_ x: Double) -> Double { x * x }
}

MathUtils.square(MathUtils.pi)   // call on the type, no instance

You use them for constants, factory methods, shared instances and helpers.

static vs class in a class

class Animal {
    static func kingdom() -> String { "Animalia" }   // cannot override
    class func sound() -> String { "..." }           // can override
    class var legs: Int { 4 }                        // computed, can override
}

class Bird: Animal {
    override class func sound() -> String { "Tweet" }
    override class var legs: Int { 2 }
    // override static func kingdom() ...  // error: cannot override static method
}

Bird.sound()    // "Tweet"
Bird.legs       // 2

Side-by-side

staticclass
Works instruct, enum, class, actor, protocolclass only
OverridableNoYes
Stored propertiesYes (static let, static var)No
Computed propertiesYesYes
DispatchStatic (direct)Dynamic (vtable)

In a class, static is the same as final class.

Why no class stored properties?

class Config {
    // class var timeout = 30   // error: class stored properties not supported
    static var timeout = 30     // OK
}

A stored type property has one storage location for the whole program. Overriding it per subclass would need separate storage, so Swift does not allow it. Use a class var computed property if subclasses must return different values.

Overriding class with static

A subclass can override a class member with static. This stops further overrides.

class Base {
    class func name() -> String { "Base" }
}

class Child: Base {
    override static func name() -> String { "Child" }   // final from here
}

// class GrandChild: Child {
//     override class func name() ...  // error
// }

In protocols, always static

Protocols use static for type requirements. A class can satisfy it with class to allow overrides.

protocol Factory {
    static func make() -> Self
}

class Vehicle: Factory {
    required init() { }
    class func make() -> Self { self.init() }
}

struct Point: Factory {
    static func make() -> Point { Point() }
}

Static stored properties are lazy and thread-safe

final class ImageCache {
    static let shared = ImageCache()
    private init() { }
}

shared is created on first access. Swift guarantees it is created only once, even with many threads. This is the common singleton pattern.

In Swift 6 language mode, a static var is global mutable state. The compiler reports it as unsafe unless it is isolated (for example @MainActor), or you change it to a let of a Sendable type.

enum AppState {
    @MainActor static var launchCount = 0   // OK in Swift 6
    static let apiVersion = "v2"            // OK: immutable and Sendable
}

When to use which

  • Use static by default. It is clearer and a little faster.
  • Use class only when you really want subclasses to change the behavior.
  • In final classes, there is no difference, so use static.

Common follow-up questions

  • Can self be used inside a static method? Yes. Inside a type method, self is the type itself, not an instance.
  • Is Self different from self? Self (capital) is the current type. In a class method it can be the subclass. self in a type method is the metatype value.
  • Can an instance method call a static method without the type name? No. You write Self.helper() or TypeName.helper().
  • Are static properties stored per subclass? No. There is one storage for the class and all subclasses.
  • Can extensions add static stored properties? Yes. Extensions cannot add instance stored properties, but static stored properties are fine.