Access Control by using Access Specifiers
iOS Developer level : Intermediate
Configuration when this article was written
Xcode : Version 11.5
MacOS : 10.15.5 (Catalina)
A working example
Final code can be found in the Resources section below.
So lets understand what do we mean by Modules and Source Files
before we head forward
Modules and Source Files
A module is a single unit of code distribution—a framework or application that is built and shipped as a single unit and that can be imported by another module with Swift’s import keyword.
In the example below (which I will coverup shortly) SharedFunctionality is a Module
A source file is a single Swift source code file within a module (in effect, a single file within an app or framework).
In the example below (which I will coverup shortly) MySimpleClass.swift is a source file
Enough of introduction , let’s be technical…
Following are the Access Specifiers supported in Swift
· Open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework.
Open access applies only to classes and class members, and it differs from public access by allowing code outside the module to subclass and override.
· Internal access (This is the default access level) enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
· File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
· Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Open access is the highest (least restrictive) access level and private access is the lowest (most restrictive) access level.
Final code can be found in the references section below. You can also see the output by downloading and running the code. Just uncomment the code (wherever mentioned) to see the impact caused by access levels
Start by creating a Single view app and name it as AccessSpecfiersInAction
Xcode -> New Project -> Single View App -> AccessSpecfiersInAction
Save.
Now add an empty swift file and name it as MySimpleClass
Add the following to the file
open class MyOpenClass {
func methodWithDefaultAccessSpecifier() {}
open func methodWithExplicitAccessSpecifier() {}
}
public class MyPublicClass {
func methodWithDefaultAccessSpecifier() {}
public func methodWithExplicitAccessSpecifier() {}
}
//Internal is the default access
internal class MyInternalClass {
func methodWithDefaultAccessSpecifier() {}
internal func methodWithExplicitAccessSpecifier() {}
}
class MyDefaultClass {
func methodWithDefaultAccessSpecifier() {}
func methodWithExplicitAccessSpecifier() {}
}
fileprivate class MyFilePrivateClass {
func methodWithDefaultAccessSpecifier() {}
fileprivate func methodWithExplicitAccessSpecifier() {}
}
private class MyPrivateClass {
func methodWithDefaultAccessSpecifier() {}
private func methodWithExplicitAccessSpecifier() {}
}
class AccessWithinSameFile {
func simpleCheckMethod() -> Void {
let openClass = MyOpenClass()
openClass.methodWithDefaultAccessSpecifier()
openClass.methodWithExplicitAccessSpecifier()
let publicClass = MyPublicClass()
publicClass.methodWithDefaultAccessSpecifier()
publicClass.methodWithExplicitAccessSpecifier()
let internalClass = MyInternalClass()
internalClass.methodWithDefaultAccessSpecifier()
internalClass.methodWithExplicitAccessSpecifier()
let defaultClass = MyDefaultClass()
defaultClass.methodWithDefaultAccessSpecifier()
defaultClass.methodWithExplicitAccessSpecifier()
let filePrivateClass = MyFilePrivateClass()
filePrivateClass.methodWithDefaultAccessSpecifier()
filePrivateClass.methodWithExplicitAccessSpecifier()
let privateClass = MyPrivateClass()
privateClass.methodWithDefaultAccessSpecifier()
privateClass.methodWithExplicitAccessSpecifier()
}
}
Now build the Xcode project . You will see error as follows
'methodWithExplicitAccessSpecifier' is inaccessible due to 'private' protection level
This because method methodWithExplicitAccessSpecifier inside MyPrivateClass is marked private and is not accessible outside MyPrivateClass.
Now comment this line
privateClass.methodWithExplicitAccessSpecifier()
and it should look like the following
// let privateClass = MyPrivateClass()
// privateClass.methodWithDefaultAccessSpecifier()// privateClass.methodWithExplicitAccessSpecifier()
Now run your code, should have no issues now
Now in ViewController.swift in the viewDidLoad() add the following
let openClass = MyOpenClass()
openClass.methodWithDefaultAccessSpecifier()
openClass.methodWithExplicitAccessSpecifier()
let publicClass = MyPublicClass()
publicClass.methodWithDefaultAccessSpecifier()
publicClass.methodWithExplicitAccessSpecifier()
let internalClass = MyInternalClass()
internalClass.methodWithDefaultAccessSpecifier()
internalClass.methodWithExplicitAccessSpecifier()
let defaultClass = MyDefaultClass()
defaultClass.methodWithDefaultAccessSpecifier()
defaultClass.methodWithExplicitAccessSpecifier()
let filePrivateClass = MyFilePrivateClass()
let privateClass = MyPrivateClass()
Now build the Xcode project . You will see error as follows
Use of unresolved identifier
This is because we cannot access any class, structure , variables etc which are marked fileprivate outside the source file where it is declared. Here the source file is MySimpleClass.swift and we are accessing in ViewController.swift.
So now we understand how fileprivate and private work.
To understand internal,public and open access specifier lets create a framework and name it SharedFunctionality
Goto
Xcode -> New Project -> Framework under iOS tab-> SharedFunctionality
Save it anywhere, in my case I will save it on Desktop
Add a new swift file and name it SharedFeature.swift to SharedFunctionality project and add the following.
import Foundation
open class ExternalOpenClass {
public init() {}
/*
We cannot use open access level to init.
open init() { // error: only classes and overridable class members can be declared 'open'; use 'public' Refer screenshot below
}*/
func methodWithDefaultAccessSpecifier() {}
open func methodWithExplicitAccessSpecifier() {}
}
public class ExternalPublicClass {
public init() {}
func methodWithDefaultAccessSpecifier() {}
public func methodWithExplicitAccessSpecifier() {}
}
internal class ExternalInternalClass {
public init() {}
func methodWithDefaultAccessSpecifier() {}
internal func methodWithExplicitAccessSpecifier() {}
}
class ExternalDefaultClass {
public init() {}
func methodWithDefaultAccessSpecifier() {}
func methodWithExplicitAccessSpecifier() {}
}
If you see the code about we have tagged init with public, This is to make it available outside this framework.
Now build the project and close the project from xcode
Open our AccessSpecfiersInAction project and add SharedFunctionality framework
- Right Click on AccessSpecfiersInAction in project navigator
- Traverse to folder where you have saved SharedFunctionality
- Open SharedFunctionality and add SharedFunctionality.xcodeproj
You can also follow as per screen shots below
After adding it should like the screenshot below
Build the project to see no errors.
Now open ViewController.swift and add the following. Lets comment out the earlier code so we can test access levels from our SharedFunctionality framework.
let openClass = ExternalOpenClass.init()
//openClass.methodWithDefaultAccessSpecifier()
openClass.methodWithExplicitAccessSpecifier()
public func methodMarkedPublic() {}
let publicClass = ExternalPublicClass()
//publicClass.methodWithDefaultAccessSpecifier()
publicClass.methodWithExplicitAccessSpecifier()
//let internalClass = ExternalInternalClass()
// let defaultClass = ExternalDefaultClass()
Now uncomment
let internalClass = ExternalInternalClass()
let defaultClass = ExternalDefaultClass()
You see that there is an error since ExternalInternalClass and ExternalDefaultClass is internal and we are accessing them outside SharedFunctionality module
So now we understand how internal access level work.
To Look at how open and public work
Create a new file in called InheritClass.swift in AccessSpecfiersInAction project.
Add the following code
class CustomOpenClass: ExternalOpenClass {
override func methodWithExplicitAccessSpecifier() {
print("I am overriden")
}
}
Now we see that We can ExternalOpenClass and also override methodWithExplicitAccessSpecifier method, Since we have added open access level.
Now Modify the code to look as below
class CustomOpenClass: ExternalOpenClass {
override func methodWithExplicitAccessSpecifier() {
print("I am overriden")
}
override func methodMarkedPublic() {
}
}
You will see error (Overriding non-open instance method outside of its defining module) when you try to override methodMarkedPublic(). This is because public methods from other module cant be overridden.
Now add the following code
class CustomPublicClass: ExternalPublicClass {
}
You will see error (Cannot inherit from non-open class 'ExternalPublicClass' outside of its defining module) when you try to subclass ExternalPublicClass(). This is because public class from other module cant be subclassed.
Lets summarise
· Open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework.
Open access applies only to classes and class members, and it differs from public access by allowing code outside the module to subclass and override.
· Internal access (This is the default access level) enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure.
· File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
· Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
We stop here ...
Resources:
https://github.com/TeaTalkInternal/AccessSpecifiers
My Reference :
https://www.youtube.com/watch?v=S7cs6nURvG0
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html
Comments
Post a Comment