Access Control in Swift Explained
In the general concept of software development, access control refers to the practice of defining the scope in which entities are available within a codebase. In Swift, these entities range from Classes to Structs, to Properties and Methods.
Access control in Swift is achieved by a set of keywords provided to us by the Swift programming language. In this article, I would delve into the details of these keywords, that is, when to use them, and the differences between similar keywords.
Open Access Level
At the pinnacle of the access hierarchy is the open
keyword, offering the broadest access. This level is tailored for crafting code meant to be utilized as libraries. Entities declared as open
are not only open to subclassing and extension but also allow method overriding. This exemplifies the core of object-oriented principles.
The example below defines a class Staff
which is declared as open
and a class Janitor
which is a subclass of the Staff
class. The Staff
class contains a method identity
which prints I am a staff
to the console. As a result of this subclassing, the Janitor
class has access to the Staff
class’s identity method. However, we see that this method can be overridden. This feature is utilized, and now the identity
method of the Janitor
class prints I am a Janitor Staff
to the console.
open class Staff {
func init(){}
func identity(){
print("I am a staff")
}
}
class Janitor: Staff {
func init(){}
override func identity(){
print("I am a Janitor Staff")
}
}
let john : Staff = Staff()
let ken : Janitor = Janitor()
john.identity() // I am a Staff
ken.identity() // I am a Janitor Staff
Public Access Level
The next tier, public
, echoes the flexibility of open
, permitting subclassing. However, it differs in not allowing method overriding. Entities designated as public
are accessible within their defining module and extend their reach to external modules that import their home module.
Consider the Staff
and Janitor
class declared below. They are similar to the Staff
and Janitor
classes defined in the open
keyword example above however Staff
is now defined as a public entity, hence the identity
method of the Staff
class can’t be overridden. A different method of the Janitor
class janitorIdentity
is now defined to print I am a Janitor Staff
, while the Janitor
class still has access to the identity
method of the Staff
class.
public class Staff {
func init(){}
func identity(){
print("I am a staff")
}
}
class Janitor: Staff {
func init(){}
func janitorIdentity(){
print("I am a Janitor Staff")
}
}
let ken : Janitor = Janitor()
ken.identity() // I am a Staff
ken.janitoIdentity() // I am a Janitor Staff
Internal Access Level
Moving one step down is the internal
access level, a gentle yet crucial access level within a module. Entities with this level can be accessed within the module they inhabit but remain sealed off from the outside modules. internal
is Swift's default access level when not explicitly stated.
FilePrivate Access Level
fileprivate
access level limits the accessibility of an entity to the source file within which it is defined. The example below demonstrates the use of the fileprivate
access level.
class Person {
var name = "John"
func introduce(){
print("Hi, I am \(name)")
}
}
fileprivate func exposeName(name){
print(name)
}
In the above example, a class Person
and a function exposeName
are created in the same file*.* Although the function is defined outside the scope of the Person
class, it still has access to the name
variable defined within the scope of the Person
class. This is because the name
variable is defined as a fileprivate
entity, hence is accessible throughout the same source file.
Private Access Level
A private access level is used when we wish to limit the accessibility of an entity to the scope in which it is defined and the extensions of that scope which exist in the same source file. When entities are defined as private a reference to that entity outside of its scope or an extension of its scope will result in an error.
Consider the example illustrated below. A class Person
is defined which has a private property name
. Outside the scope of the Person
class, a function exposeName
which tries to access the name
property of the Person
class is also defined. This attempt to access the name property results in an error.
class Person {
private var name = "John"
func introduce(){
print("Hi, I am \(name)")
}
}
func exposeName(){
print(name) // Cannot access the name property, as it is private.
}
Major Differences Between Open and Public
Keyword | Accessibility | Subclassing | Overriding |
public | Within its module and any module that imports its defining module. | Allowed within its module and any module that imports its defining module | Allowed within its module and any module that imports its defining module |
open | Any module | Allowed | Allowed |
Major Differences between File-Private and Private
Keyword | Accessibility | Subclassing | Overriding |
private | Within the scope it is defined | Not Allowed | Not Allowed |
fileprivate | Within the source file it is defined | Not Allowed | Not Allowed |
Conclusion
Congratulations on getting to the end of this article. As you continue your journey through Swift’s realms, remember that access control wields the power to sculpt your code’s architecture and fortify its security. Each level serves a unique purpose, protecting the integrity of your software.
Subscribe to my newsletter
Read articles from Hussein Tijani directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Hussein Tijani
Hussein Tijani
Hussein Tijani is a Software Engineer based in Lagos, Nigeria. His area of expertise encompasses the entire product development cycle, API design and development and third-party integrations. He is passionate about sharing his Software Engineering knowledge through technical articles, verbal conversations and social media.