Everyone seems to be bullying on the poor singleton sample, the general public name it anti-pattern. However what precisely is a singleton class and why is it so dangerous?
What’s a singleton?
It is a very fashionable and generally adopted sample due to simplicity. A singleton class can solely have precisely one occasion via the complete software lifecycle. That single occasion is simply accessible via a static property and the initialized object is often shared globally. It is like a world variable. 🌏
International variables and states
Singletons have dangerous repute as a result of they share world mutable states. The worldwide key phrase is at all times feared even within the circle of skilled builders. International states & variables are the hotbed of unintended effects. International variables might be accessed from wherever of your program so your courses that use them will grow to be stateful, unsecure, tight coupled and onerous to debug. It isn’t a superb apply to share states alongside objects via this fashion for apparent causes. 🤮
Unwanted side effects
You must scope and isolate your variables as a lot as you possibly can and reduce the statefullness of your code. This may remove unintended effects, make your code safer to make use of. Contemplate the next instance:
var world = 0
func sq.(_ x: Int) -> Int {
world = x
return x * x
}
world = 1;
var consequence = sq.(5)
consequence += world
print(consequence)
The sq. technique is written by another person, who needed to retailer the enter in the identical world variable for some purpose. Now once you name that operate you will not be avare of this, till you take a look at his code. Think about this sort of points within a challenge with a lot of oop courses written by a number of code authors… good luck with the military of BUGS! 🐛🐛🐛
The key lifetime of a singleton object
Singletons are created as soon as and dwell eternally, they work nearly precisely like world variables and that is why you must be extraordinarily cautious with them. You must solely handle these states with singletons that lasts for the entire lifecycle of the app. For instance user-specific classes are normally dangerous practices and it is best to rethink your design. Additionally Swift just isn’t thread protected by default, so if you’re working with singletons you must be ready for multi-threading points as properly. But when they’re so problematic, should not we merely keep away from them solely? The reply isn’t any. 🚫
When to make use of a singleton class?
For instance UIApplication is most probably a singleton as a result of there must be just one software occasion, and it ought to dwell till you shut it down. That makes simply the right instance for a singleton. One other use case generally is a Logger class. It is protected to make use of a singleton as a result of your software will not behave any totally different if a logger is turned on or not. Noone else will personal or handle the logger and you will solely cross data into the logger, so states cannot be tousled. Conclusion: a console or a logger class is sort of an appropriate state of affairs for the utilization of the singleton sample. 👏
Console.default.discover("Hey I am a singleton!")
There are a a lot of “singletonish” (not all the pieces is a real singleton object) use instances in Apple frameworks, here’s a quick checklist, so you possibly can have somewhat inspiration:
- HTTPCookieStorage.shared
- URLCredentialStorage.shared
- URLSessionConfiguration.default
- URLSession.shared
- FileManager.default
- Bundle.important
- UserDefaults.commonplace
- NotificationCenter.default
- UIScreen.important
- UIDevice.present
- UIApplication.shared
- MPMusicPlayerController.systemMusicPlayer
- GKLocalPlayer.localPlayer()
- SKPaymentQueue.default()
- WCSession.default
- CKContainer.default()
- and so on.
I’ve seen a lot of supervisor courses carried out as singletons, reminiscent of community, location or core knowledge managers, however these objects normally should not be singletons, just because it may be a couple of of them. 💩
Singleton sample might be very helpful, however it must be used with warning
If you wish to flip one thing right into a singleton, ask your self these questions:
Will anything personal, handle or be liable for it? Is there going to be precisely one occasion?
- Will it’s a world state variable?
- Ought to I actually use a globally shared object?
- Ought to dwell via the entire app lifecycle?
- Is there any alternate options for it?
If the solutions is clearly a sure for all the pieces above, then you possibly can “safely” use a singleton or a world variable to retailer your knowledge. 🎉🎉🎉
How one can create a singleton in Swift?
It is very easy to make a singleton object in Swift, however please at all times assume twice and contemplate alternate options earlier than you apply this design sample.
class Singleton {
static let shared = Singleton()
non-public init() {
}
}
let singleton = Singleton.shared
These days I am at all times creating one particular singleton object, that is known as App. This manner I can hook up each software associated world state properties into that one singleton. The naming conference additionally helps me to reevaluate what goes into it. 💡
How one can remove singletons?
If there may be different method it is best to go together with that in ~90% of the instances. The commonest various resolution for singletons is dependency injection. First it is best to summary the singleton strategies right into a protocol, then you should use the singleton because the default implementation if it is nonetheless wanted. Now you possibly can inject the singleton or a refactored object into the appropriate place. This manner your code might be examined with mocked objects of the protocol, even ignoring the singleton itself. 😎
typealias DataCompletionBlock = (Knowledge?) -> Void
protocol Session {
func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock)
}
extension URLSession: Session {
func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
let job = self.dataTask(with: request) { knowledge, _, _ in
completionHandler(knowledge)
}
job.resume()
}
}
class ApiService {
var session: Session
init(session: Session = URLSession.shared) {
self.session = session
}
func load(_ request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
self.session.make(request: request, completionHandler: completionHandler)
}
}
class MockedSession: Session {
func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
completionHandler("Mocked knowledge response".knowledge(utilizing: .utf8))
}
}
func take a look at() {
let api = ApiService(session: MockedSession())
let request = URLRequest(url: URL(string: "https://localhost/")!)
api.load(request) { knowledge in
print(String(knowledge: knowledge!, encoding: .utf8)!)
}
}
take a look at()
As you possibly can see the singleton sample may be very simple to implement, however it’s actually onerous to decide about it is software kinds. I am not saying that it is an anti-pattern, as a result of it is clearly not, however take care if you’re planning to cope with singletons. 😉