Select Page

The Core Data stack we have built in this series is slowly taking shape. With every iteration, we add a dash of complexity in return for a few key advantages.

The Core Data stack is set up and managed by the CoreDataManager class. It provides access to the main managed object context, neatly hiding the private managed object context, the managed object model, and the persistent store coordinator from the rest of the application.

One Instance To Rule Them All

The majority of applications need only one Core Data stack, which implies that a single instance of the CoreDataManager class suffices. At the mention of the word single, a surprising number of developers have the urge to turn the CoreDataManager instance into a singleton. While I don’t have any objections against the singleton pattern, I have a strong opinion about the motivation for using this controversial pattern.

“In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.” — Wikipedia

Does the above definition of the singleton pattern surprise you? Many developers create singletons for a different reason, that is, providing easy access to the singleton object. This, however, is a byproduct of the singleton pattern. It is not the goal of the singleton pattern.

By easy access I mean of course global access. That is the motivation many developers have when using the singleton pattern. But what is wrong with a globally accessible object?

While I don’t want to dive into the details of why globals are a bad idea, I do want to emphasize the drawbacks of the singleton pattern when used incorrectly, that is, for global access to a single object.

Making an object globally accessible instead of passing it around is considered an anti-pattern. Whenever you are about to create a singleton, consider why it is necessary to make it a globally accessible object. Is it convenience? Is there an alternative approach that also solves the problem?

Another reason for not turning the CoreDataManager instance into a singleton is dependency management. Globally accessible objects that are used in various places of a project obfuscate the dependencies of the project. A much better approach is to use dependency injection. I have written several articles about dependency injection because I really enjoy the simplicity and transparency of dependency injection. It is much easier than most developers think. I like to quote James Shore whenever I discuss dependency injection.

“Dependency injection is a 25-dollar term for a 5-cent concept.” — James Shore

Injecting the Core Data Manager

In this lesson, I would like to show you how easy it is to make the CoreDataManager instance accessible in other parts of the application without turning to the singleton pattern. Remember that we created the CoreDataManager instance in the project’s AppDelegate class. You can clone or download the project from GitHub if you need to refresh your mind.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    let coreDataManager = CoreDataManager(modelName: "DataModel")

    ...
    
}

How do we pass the Core Data manager of the application delegate to the ViewController instance, the initial view controller of the storyboard? It is tempting to create a singleton and access it with a convenience method. We can also resolve this problem with dependency injection.

We start by declaring a property for the CoreDataManager instance in the ViewController class. The first benefit of this approach immediately becomes apparent. By declaring an internal property for the CoreDataManager class, we define the CoreDataManager class as a dependency of the ViewController class.

import UIKit

class ViewController: UIViewController {

    var coreDataManager: CoreDataManager!

}

Note that the coreDataManager property is of type CoreDataManager!, an implicitly unwrapped optional. Because the coreDataManager property is essential for the ViewController class, it is fine to declare it as an implicitly unwrapped optional. If the coreDataManager property is nil when the view controller tries to access it, we have bigger problems to worry about.

We set the coreDataManager property in the application(_:didFinishLaunchingWithOptions:) method of the AppDelegate class. We slightly refactor the application(_:didFinishLaunchingWithOptions:) method as you can see below.

// MARK: - Application Life Cycle

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Initialize Storyboard
    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())

    // Instantiate Initial View Controller
    if let viewController = storyboard.instantiateInitialViewController() as? ViewController {
        // Configure View Controller
        viewController.coreDataManager = coreDataManager

        // Set Root View Controller
        window?.rootViewController = viewController
    }

    return true
}

We load the storyboard and instantiate the initial view controller. We set the coreDataManager property of the initial view controller and set the view controller as the root view controller of the window.

To prove that everything works as advertised, add the following viewDidLoad() implementation to the ViewController class. Run the application in the simulator and inspect the output in the console.

// MARK: - View Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()

    print(coreDataManager.mainManagedObjectContext)
}

Are We Jumping Through Hoops

It seems as if we are unnecessarily jumping through hoops. If the above arguments haven’t convinced you of avoiding singletons for the purpose of easy access, then consider testing. By injecting the CoreDataManager instance into the ViewController class, testing becomes much easier. It is, for example, easy to replace the CoreDataManager instance with a mock object.

Whenever I write about singletons, I emphasize that the singleton pattern isn’t a code smell per se. Using the singleton pattern to provide global access to the singleton object is. The next time you are about to create a singleton, give dependency injection a try. You will be surprised by how easy it is to adopt.

Question? Leave them in the comments below or reach out to me on Twitter. You can download the source files form GitHub.

<< Keeping It PrivateGive It Time >>