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.

As someone who has worked extensively with Swift, I've gathered many insights over the years. I've compiled them in my book Swift Gems, which is packed with advanced tips and techniques to help intermediate and advanced Swift developers enhance their coding skills. From optimizing collections and handling strings to mastering asynchronous programming and debugging, "Swift Gems" provides practical advice to elevate your Swift development. Grab your copy of Swift Gems and let's explore the advanced techniques together.

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Level up your Swift skills!$35

100+ tips to take your Swift code to the next level

Swift Gemsby Natalia Panferova

  • Advanced Swift techniques for experienced developers bypassing basic tutorials
  • Curated, actionable tips ready for immediate integration into any Swift project
  • Strategies to improve code quality, structure, and performance across all platforms

Level up your Swift skills!

100+ tips to take your Swift code to the next level

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Swift Gems

by Natalia Panferova

$35