Private and Public At the Same Time

Private and Public At the Same Time

In today's tutorial, I want to show you a little know feature of access control in Swift. You probably know that the Swift language defines five access levels:

  • open
  • public
  • internal
  • fileprivate
  • private

But what do you do if you want to declare a property internal, public, or open, but you don't want anyone but the instance to set the property? You could solve this problem declaring the property private and defining a computed property that acts as a getter for the private property.

class CoreDataManager {

    ...

    private lazy var mainManagedObjectContext: NSManagedObjectContext = {
        // Initialize Managed Object Context
        let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

        // Configure Managed Object Context
        managedObjectContext.parent = self.privateManagedObjectContext

        return managedObjectContext
    }()

    public var managedObjectContext: NSManagedObjectContext {
        return mainManagedObjectContext
    }

    ...

}

The mainManagedObjectContext property of the CoreDataManager class is declared private and we define a public computed property that provides access to the mainManagedObjectContext property. The result is that the mainManagedObjectContext can only be set by the CoreDataManager instance, but it can be accessed through the managedObjectContext computed property by other objects.

This works fine. But Swift wouldn't be Swift if there wasn't a more elegant solution. Take a look at the updated example below. This is an example from Building the Perfect Core Data Stack.

class CoreDataManager {

    ...

    public private(set) lazy var mainManagedObjectContext: NSManagedObjectContext = {
        // Initialize Managed Object Context
        let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

        // Configure Managed Object Context
        managedObjectContext.parent = self.privateManagedObjectContext

        return managedObjectContext
    }()

    ...

}

The mainManagedObjectContext property is declared public, which means we can remove the managedObjectContext computed property. To make sure only the CoreDataManager instance can set the mainManagedObjectContext property we assign the setter a lower access level by using the private(set) modifier. This also works for fileprivate and internal access levels.