Dispatch_once in Swift: Main Mistakes and Correct Alternatives

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Dispatch_once in Swift: Why It's a Mistake and How to Write a Proper Singleton

\n\n

If you've ever wondered "How to make a singleton in Swift using dispatch_once?" — you're not alone. On Stack Overflow, this question has gathered thousands of views, and in the code of many projects, strange constructs still appear that try to emulate the old Objective-C approach. But the good news is that Swift has a much more elegant and safe solution. The bad news: many developers continue to make the same mistakes, trying to transfer habits from Objective-C to Swift.

\n\n

In this article, we'll break down the top 5 mistakes when using dispatch_once in Swift, show how to avoid them, and provide a ready-made template for a proper singleton. Let's go!

\n\n

What is dispatch_once and why is it not needed in Swift?

\n\n

dispatch_once is a function from Grand Central Dispatch (GCD) that guarantees a block of code will execute exactly once during the entire application's runtime. In Objective-C, this was the standard way to create thread-safe singletons:

\n\n
+ (instancetype)sharedInstance {\n    static MyClass *sharedInstance = nil;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        sharedInstance = [[MyClass alloc] init];\n    });\n    return sharedInstance;\n}
\n\n

However, in Swift, starting from version 1.0, dispatch_once is not supported for use in user code. Yes, you can import Dispatch and try to call it, but the compiler will not allow it. Why? Because Swift offers built-in features that make dispatch_once unnecessary.

\n\n

Top 5 Mistakes When Using dispatch_once in Swift

\n\n

Mistake 1: Trying to call dispatch_once directly

\n\n

The most common mistake is trying to write something like:

\n\n
class MySingleton {\n    static var shared: MySingleton = {\n        let instance = MySingleton()\n        // configuration\n        return instance\n    }()\n    \n    private init() {}\n}
\n\n

At first glance, this works. But this is not dispatch_once. It's just a global variable that is initialized lazily on first access. Swift guarantees thread safety for such static properties, but it's not the same as dispatch_once.

\n\n

Why is this bad? Because you lose control over the initialization moment. If your singleton depends on external resources (e.g., a database or network), they might not be ready yet.

\n\n

Mistake 2: Using dispatch_once through an Objective-C wrapper

\n\n

Some developers create a bridge to Objective-C to call dispatch_once from Swift:

\n\n
// Objective-C file\n@interface OnceHelper : NSObject\n+ (void)performOnce:(void(^)(void))block;\n@end\n\n@implementation OnceHelper\nstatic dispatch_once_t onceToken;\n+ (void)performOnce:(void(^)(void))block {\n    dispatch_once(&onceToken, block);\n}\n@end
\n\n

Then in Swift:

\n\n
class MySingleton {\n    static var shared: MySingleton = {\n        var instance: MySingleton!\n        OnceHelper.performOnce {\n            instance = MySingleton()\n        }\n        return instance\n    }()\n}
\n\n

What's the problem? First, it's a crutch. Second, dispatch_once_t is a global variable, and if you forget to reset the token (e.g., during testing), the singleton will not be recreated. Third, Apple explicitly recommends not using dispatch_once in Swift.

\n\n

Mistake 3: Forgetting about thread safety

\n\n

Some write a singleton like this:

\n\n
class MySingleton {\n    static var shared: MySingleton?\n    \n    private init() {}\n    \n    static func getInstance() -> MySingleton {\n        if shared == nil {\n            shared = MySingleton()\n        }\n        return shared!\n    }\n}<                

Blogs

Book Recommendations