3
\$\begingroup\$

I am creating an iOS app and I want to implement Singleton Pattern. I have created "Singleton" LibraryAPI to act as an entry point UserManager object to get data from web API. after that I used a facade Pattern "method" to call UserManager implementation.

final class LibraryAPI {

    static let shared  =  LibraryAPI()
    private let userManager  =  UsersManager()
    private let isOnline  =  false 

    private init(){

    }

    func getUsers() -> [User] {
        return userManager.getUsers()
    }
}

my questions:

  1. if I have another Manager Class like "album" class should I use the same LibraryAPI and it will become a monolithic class and how to avoid that?
  2. should I create a "Singleton" LibraryAPI class for each manager object like UserLibraryAPI and albumLibraryAPI?

Note: any references or articles are welcome :)

\$\endgroup\$
1
  • \$\begingroup\$ I think this question falls out of scope for Code Review as it's about best practices, which falls into the don't ask section of the help center. \$\endgroup\$
    – IEatBagels
    Aug 13 '19 at 13:02
1
+150
\$\begingroup\$

Developing singletons isn't a good idea in most cases .Sure, you save some time taking this shortcut, but this code will bite you back later when the architecture of your app grows or when you add unit testing to your project. Here are a few reasons why you should NOT develop singletons:

Note: These are excerpts from this article about swift singletons.

  • Singletons provide a globally mutable shared state. Ironically, the definition of singleton is one of the reasons why singletons are bad. The global accessibility of singletons makes shared resources accessible from anywhere, especially from code that should not have any access. Even value types like Swift structures and enumerations can access a singleton, which is a bad practice. When any part of an app can access or change the global state, you get weird and hard to fix bugs.
  • Singletons carry state around for the lifetime of the application. There are cases in which you need to reset the shared state. When you can have multiple instances, you can discard the old one and then create a new one. In a singleton, instead, resetting state might not be so natural and might require specific and complex code.
  • Singleton classes often become monoliths. This exactly correlates to your concern. Since it’s easy to access a singleton from anywhere, the chances are high that code that needs to be shared ends inside an existing singleton. Massive view controllers are not the only monolithic objects you should avoid in iOS. The same happens to singletons.

If singletons are the wrong solution, what is then the correct one? The critical point here is the distinction between singletons and shared resources. In any real app, shared resources are necessary and unavoidable. There are always parts of an app’s architecture that need to be accessed from many places. Some examples are:

  • The current global state of the app.
  • The disk storage where data is saved, be it the file system, a database, the user defaults of the app, or a Core Data managed object context.
  • A URL session that groups related network requests.
  • A shared operation queue to prioritize, sequence, and schedule the asynchronous tasks of the app.

Conclusions

There are many articles online which try to answer the question: “when is it ok to use a singleton?”

My answer is: never.

That might sound a bit strict, but the drawbacks of singletons outweigh the little benefits of taking the shortcut. You can, and should, always solve the problem using dependency injection.

\$\endgroup\$
2
\$\begingroup\$

This seems to be an extremely common way of handling networking in iOS and Swift probably because it is very simple to reason about a single global singleton object for services.

I would recommend thinking carefully about using a singleton.

Why not just have a NetworkManager object that you initialize?

Even better why not separate out the services into a few logical classes? One service per class may get a bit arduous, but if you think critically you will likely be able to group services together appropriately. If you then find yourself with many Service objects, you can combine these objects with a facade pattern, that groups functionality when appropriate for a given View/ViewModel.

You will want to use classes, or structs with non static functions because when something isn't a singleton you can use dependency injection. Instead of accessing the static methods of your singleton you can inject a NetworkProvider object into the class in the constructor that uses it. This will make your code far more robust and testable. The injected object, if you created an appropriate Protocol, can be mocked.

Personally, I prefer something like Moya https://github.com/Moya/Moya/blob/master/docs/CommunityProjects.md. Its very swifty, logical, and well formatted. It also makes the relationships between services very clear. As your project gets larger, you will want to create multiple provider objects, but it provides a very concise way of writing your service layer.

Summary - Use dependency injection and normal non static structs or classes. If you have multiple services, separate them out into a separate struct/class based on similarity or simply separate out one service into a single struct or class. A library like Moya can help to structure your code to make it more readable.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.