Swift’s access control mechanism is designed to provide a way to restrict access to parts of your code from other parts of your code, as well as from external codebases. This ensures that internal implementation details remain hidden, creating a clean public API and promoting modularity and encapsulation. In this article, we’ll explore the different access control levels provided in Swift and how to use them effectively.
Swift has five access control levels, listed below from the most restrictive to the least restrictive:
private
fileprivate
internal
public
open
private
access restricts the accessibility of an entity to the enclosing declaration, and to extensions of that declaration within the same file. This access level is the most restrictive, and it is intended for use when an entity should only be available within a specific context.
class MyClass {
private var privateVar = "This variable is accessible only within MyClass"
func accessPrivateVar() {
print(privateVar) // This is OK because we're inside MyClass
}
}
extension MyClass {
func anotherAccessToPrivateVar() {
print(privateVar) // This is OK because we're in an extension of MyClass within the same file
}
}
let myInstance = MyClass()
myInstance.accessPrivateVar() // This will print the value of privateVar
myInstance.privateVar // This will cause a compile-time error because privateVar is not accessible outside of MyClass
fileprivate
access restricts the accessibility of an entity to its defining source file. This access level is useful when you want to make an entity available to other types defined within the same file but prevent access from other files.
class MyClass {
fileprivate var fileprivateVar = "This variable is accessible only within the same file"
}
class AnotherClassInSameFile {
func accessFileprivateVar() {
let myInstance = MyClass()
print(myInstance.fileprivateVar) // This is OK because we're inside the same file
}
}
let anotherInstance = AnotherClassInSameFile()
anotherInstance.accessFileprivateVar() // This will print the value of fileprivateVar
internal
access allows an entity to be accessed within the entire module that includes the definition of the entity. This access level is the default if you do not explicitly specify an access level.
// In ModuleA
class MyClass {
internal var internalVar = "This variable is accessible within the same module"
}
let myInstance = MyClass()
print(myInstance.internalVar) // This is OK because we're inside the same module (ModuleA)
// In ModuleB, which imports ModuleA
import ModuleA
let anotherInstance = MyClass()
print(anotherInstance.internalVar) // This will cause a compile-time error because internalVar is not accessible outside of ModuleA
public
access allows an entity to be accessed from any source file within the module that includes the definition of the entity, as well as from any external module that imports the module containing the entity. However, subclasses and overrides of public
entities can only be created within the defining module.
// In ModuleA
public class MyClass {
public var publicVar = "This variable is accessible from any module that imports ModuleA"
}
// In ModuleB, which imports ModuleA
import ModuleA
let myInstance = MyClass()
print(myInstance.publicVar) // This is OK because publicVar is accessible from any module that imports ModuleA
open
access is the least restrictive access level, allowing an entity to be accessed from any source file within the module that includes the definition of the entity, as well as from any external module that imports the module containing the entity. Unlike public
access, open
access allows subclasses and overrides of the entity to be created in any module that imports the defining module.
// In ModuleA
open class MyBaseClass {
open var openVar = "This variable is accessible from any module that imports ModuleA and can be subclassed and overridden"
}
// In ModuleB, which imports ModuleA
import ModuleA
class MyDerivedClass: MyBaseClass {
override var openVar: String {
didSet {
print("The value of openVar has changed")
}
}
}
let myInstance = MyDerivedClass()
myInstance.openVar = "New value" // This is OK because openVar is accessible from any module that imports ModuleA and can be subclassed and overridden
Swift’s access control mechanism allows developers to manage the visibility of their code, ensuring that internal implementation details remain hidden and promoting modularity and encapsulation. By understanding the different access control levels and how they interact, you can create more robust and maintainable Swift applications.