The Singleton Pattern: One Instance to Rule Them All

A singleton ensures a class has only one instance and provides a global access point to it. It's used for shared resources like a network manager or logger. The main footgun is that overuse creates tight coupling and makes testing difficult.
WHY IT EXISTS: Some resources are naturally singular or need a single point of control to avoid conflicting states. For example, an application often needs exactly one object to manage all network requests, one to handle user authentication state, or one to coordinate logging. The Singleton pattern was created to enforce this "one and only one" rule programmatically, preventing accidental creation of multiple instances.
THE MENTAL MODEL: Think of a singleton as the designated manager for a specific department in a company, like HR. No matter which employee from which team has an HR-related question (needs access to the resource), they all go to the same HR manager (the singleton instance). This ensures all HR activities are coordinated through a single, known, and authoritative entity, preventing chaos and inconsistent policies.
HOW IT WORKS: In modern Swift, you create a singleton by declaring a static constant property on the class, which holds the single instance. The class's initializer is marked as private to prevent anyone else from creating new instances directly. For example: static let shared = MyManager(). The Swift runtime ensures that this static property is initialized lazily and thread-safely the very first time it's accessed. There's no need for manual locking or complex setup.
WHEN TO USE IT: Use a singleton when you must ensure only one instance of a class exists to coordinate actions across a system. It's suitable for managing access to a shared resource that is fundamentally singular. Good candidates are managers for services like logging or analytics. Apple's own frameworks use this pattern extensively, for example, with URLSession.shared, FileManager.default, and UserDefaults.standard.
WHEN NOT TO USE IT: Avoid singletons for objects that hold complex, mutable state, or for core business logic components. The global nature of singletons can create tight coupling between different parts of your application, making them difficult to reuse or test in isolation. Because the instance is shared globally, state can leak between tests, causing unpredictable failures. Dependency injection is often a more flexible and testable alternative.
ONE CANONICAL EXAMPLE: A simple network manager is a classic singleton. class NetworkManager { static let shared = NetworkManager() private init() { // Configuration setup for the network layer } func fetchData(from url: URL) { // Logic to fetch data print("Fetching data from \(url)...") } } To use it from anywhere in the app: NetworkManager.shared.fetchData(from: someURL)
Read the original → developer.apple.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.