Access Control: Part 2

The Resistance of Swift


Dhayaalan Raju

2 years ago | 4 min read

To check out the topics covered in Part 1 of Access Control click here

The Topics covered in this post are

  1. Access Control on Getters and Setters
  2. Access Control on Initializers
  3. Access Control on Protocols
  4. Access Control on Extensions

1. Access Control on Getters and Setters

Getters and setters are a part of computed properties. The didSet and willSet performs any extra tasks when a computed property is about to change or has changed. Getters and setters provide the logic to set the value or retrieve the value.

Getters and setters for constants, variables, properties, and subscripts automatically receive the same access level as the constant, variable, property, or subscript they belong to.

A setter can be given a lower access level than its corresponding getter, in order to restrict the read-write scope of that variable, property, or subscript. A lower access level can be given by writing fileprivate(set)private(set), or internal(set) before the var or subscript introducer.


This rule applies to stored properties as well as computed properties.

The example below defines a structure called TrackedString, which keeps track of the number of times a string property is modified:

struct TrackedString {
private(set) var numberOfEdits = 0
var value: String = "" {
didSet {
numberOfEdits += 1

The TrackedString structure defines a stored string property called value, with an initial value of "" (an empty string). The structure also defines a stored integer property called numberOfEdits, which is used to track the number of times that value is modified. This modification tracking is implemented with the help of didSet property observer on the value, which increments numberOfEdits every time the value property is set to a new value.

The TrackedString structure and the value doesn’t provide an explicit access-level modifier, and so they both receive the default access level of internal. However, the access level for the numberOfEdits property is marked with a private(set) modifier to indicate that the property’s getter still has the default access level of internal, but the property is settable only from within code that’s part of the TrackedString structure. This enables TrackedString to modify the numberOfEdits internally, but to present the property as a read-only property when it’s used outside the structure’s definition.

When a TrackedString instance is created and it is modified few times then it can be seen that numberOfEdits value updates to match the number of modifications

var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked."
stringToEdit.value += "This edit will increment numberOfEdits."
stringToEdit.value += "So will this one."
print("The number of edits is \(stringToEdit.numberOfEdits)")

This prints “The number of edits is 3”

Although the current value of the numberOfEdits property from within another source file can be accessed, it cannot be modified from another source file.

The example below shows a version of the TrackedString structure in which the structure is defined with an explicit access level of public. The structure’s members (including the numberOfEdits) therefore have an internal access level by default. The structure’s numberOfEdits property getter can be made public, and its property setter private, by combining the public and private(set) access-level modifiers

public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = ""{
didSet {
numberOfEdits += 1
public init() {}

2. Access Control on Initializers and Default Initializers

Custom initializers can be assigned an access level less than or equal to the type that they initialize. The only exception is for the required initializers.

Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer

A required initializer must have the same access level as the class it belongs to.

Default Initializers

Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.

A default initializer has the same access level as the type it initializes unless that type is defined as public. For a type that is defined as public, the default initializer is considered internal.

Default Memberwise Initializers for Structure Types

The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, then the initializer is also a file private. Otherwise, the initializer has an access level of internal.

3. Access Control on Protocols

To assign an explicit access level to a protocol type, it is done when defining it in the first place. This enables to create protocols that can only be adopted within a certain access context.

The access level of each requirement within a protocol definition is automatically set to the same access level as the protocol. A protocol requirement can’t be set to a different access level than the protocol it supports. This ensures that all of the protocol’s requirements will be visible on any type that adopts the protocol.


When a public protocol is defined, the protocol’s requirements require a public access level for those requirements when they’re implemented.

Protocol Inheritance

When a new protocol is defined that inherits from an existing protocol, the new protocol can have at most the same access level as the protocol that it inherits from.

For example, A public protocol cannot be written that inherits from an internal protocol.

4. Access Control on Extensions

A class, structure, or enumeration can be extended in any access context in which the class, structure, or enumeration is available. Any type members added in that extension will have the same default access level as type members declared in the original type being extended.

  • When extending a public or internal type, any new type members added will have a default access level of internal.
  • When extending a file-private type, any new type members added will have a default access level of file private.
  • When extending a private type, any new type members added will have a default access level of private.

Alternatively, an extension can be marked with an explicit access-level modifier (like private) to set a new default access level for all members defined within the extension. This new default can still be overridden within the extension for individual type members.

So far concepts on Access control on Getters and Setters, Initializers, Protocols, and Extensions are covered.


Created by

Dhayaalan Raju

iOS Developer at Ivy Mobility







Related Articles