Use cases for self, Self and Self.self in Swift

The Swift language constructs self, Self, and Self.self can sometimes be a source of confusion, even for experienced developers. It's not uncommon to pause and recall what each is referring to in different contexts. In this post I aim to provide some straightforward examples that clarify the distinct roles and uses of these three constructs. Whether it's managing instance references, adhering to protocol conformance, or accessing metatype information, understanding these concepts is key to harnessing the full potential of Swift in our projects.

# self - instance reference

self refers to the instance of the type within its own instance methods. It's a way to access the instance's properties and methods from within its own scope. This is particularly useful for differentiating between instance properties and method parameters when they share the same name.

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

# Self - type reference in protocols

Self with a capital "S" refers to the type that conforms to a protocol, allowing for polymorphic behavior. It enables protocols to specify requirements that are then tailored to the conforming type, a feature that's especially powerful in the context of protocol-oriented design.

protocol Duplicatable {
    func duplicate() -> Self
}

In the Duplicatable protocol, Self is used to specify that the duplicate() method should return an instance of the conforming type. The exact type is not specified in the protocol itself, but will be determined by the type that conforms to the protocol. It allows each conforming type to have a clear contract: if you conform to Duplicatable, you must implement a duplicate() method that returns an instance of your own type.

# Self.self - metatype reference

Self.self is used to refer to the metatype of the type, essentially the type of the type itself. It's commonly used in static methods or when we need to access type-level properties or pass the type itself as a parameter.

protocol Registrable {
    static func register()
}

extension Registrable {
    static func register() {
        print("Registering \(Self.self)")
    }
}

class Service: Registrable {}

// Prints `Registering Service`
Service.register()

In this example, Self.self is used to access the metatype of the conforming type (Service) within a static method, allowing for type-level operations.

Understanding and correctly utilizing self, Self, and Self.self are fundamental in harnessing the full potential of Swift's type system and protocol-oriented programming capabilities. Each serves a specific purpose, enabling more expressive, flexible, and maintainable code.

Integrating SwiftUI into UIKit Apps by Natalia Panferova book coverIntegrating SwiftUI into UIKit Apps by Natalia Panferova book cover

Check out our book!

Integrating SwiftUI into UIKit Apps

Integrating SwiftUI intoUIKit Apps

UPDATED FOR iOS 17!

A detailed guide on gradually adopting SwiftUI in UIKit projects.

  • Discover various ways to add SwiftUI views to existing UIKit projects
  • Use Xcode previews when designing and building UI
  • Update your UIKit apps with new features such as Swift Charts and Lock Screen widgets
  • Migrate larger parts of your apps to SwiftUI while reusing views and controllers built in UIKit