Swift Access Control

Filed Under: Swift
swift-access-control-flow

In this tutorial, we’ll discuss about Swift Access Control. Before we get a hang of it, let’s understand an important term that we’ll be using throughout this tutorial: Modules.

Modules are just a bundle of code that is used in other Modules through the import statement. In simpler terms, a Module is just a folder in which you hold your swift files.

Swift Access Control

Access Control handles the restrictions of your code. By defining the access of your classes, functions, properties, enumerations, protocols, you have the power to use these stuff at places you want to, and hide it from where you don’t need it, in your XCode Project.

Note: Whenever we write entities in this tutorial, it’ll refer to these: classes, functions, properties, enumerations, protocols.

Swift Access Levels

Following are the major levels that provide you with access control. They’re ordered from least restricted to the highest.

  • open: Least restrictive. Entities can be accessed from any file in the current module or a different module files.
    classes. Classes/Members with open modifier can be inherited/overridden anywhere in the current module or different module.
  • public: Less restrictive. Entities can be accessed in the same or different module.
    Classes/Members with open modifier cannot be inherited/overridden anywhere in modules other than the current module.
  • internal: Default access level. If no access level is specified, this is the default access level. It allows entities to be used within any source file in their defined module but not outside that module. The same rule applies to inheritance too.
  • fileprivate: This access level restricts the use of entities to the current file only. Subclassing a fileprivate class would require you to specify the subclass access level to fileprivate or a more restrictive one.
  • private: Most restrictive. Entities with this access level can be accessed only in the enclosed scope. Also, any extension to that declaration has the access too.

The following flow aptly shows the access levels for each.

swift access control

Swift Access Control Example

The syntax of classes and properties looks like this:


open class AnOpenClass {}
public class APublicClass {}
internal class ExplicitInternalClass {}
fileprivate class AFilePrivateClass {}
private class APrivateClass {}

open func AnOpenFunction() {} 
public var APublicVariable = 0
internal let ExplicitInternalConstant = 0
fileprivate func AFilePrivateFunction() {}
private func APrivateFunction() {}

func ImplicitInternalFunction(){}
class ImplicitInternalClass {}

Swift Access Control – Properties

A Property – var, let cannot have more access that the type it is assigned to.


class A {
    
}
public var a = A() //compiler error.

open class B{
 //Your code
}
public var b = B()

private class C{
    //Your code
}
private var c = C()

class A is internal, so it’ll fail to compile when a is public(more access)
A private class has to have a private property defined.

Swift Access Control Getter and Setter Methods

The getters and setters on a property would get the same access level as defined on the property.
We can set a different access level on the setter too.
But, the setter cannot have a higher access level than the property.
A fileprivate getter and setter property:


var length : Double = 5.0
let breadth : Double = 1.1

fileprivate var area : Double {
    
    get{
        return length*breadth
    }
    set(newArea)
    {
        length = newArea/breadth
    }
}

The getter will always have the same access level as the property.
The following is a public getter and a private setter.


var length : Double = 5.0
let breadth : Double = 1.1

public private(set) var area : Double {
    
    get{
        return length*breadth
    }
    set(newArea)
    {
        length = newArea/breadth
    }
}

The following would fail since the setter has more access than the property and its getter


var length : Double = 5.0
let breadth : Double = 1.1

private fileprivate(set) var area : Double { //compiler error
    
    get{
        return length*breadth
    }
    set(newArea)
    {
        length = newArea/breadth
    }
}

Swift Access Control – Classes

The access level defined for a class may or may not be applied to the members and functions.
A class with public access means that its members will have internal access by default unless stated otherwise.
The following image shows two classes. The private members of one class cannot be accessed in the other.
swift access control classes

Access control with Subclasses

Rule 1: A subclass cannot have a higher access level than it’s superclass.

The following example won’t compile.


class A {
   //Internal class
}

public class B : A {
 //public subclass not allowed.
}
Rule 2: Overridden methods can have a higher access level than their superclass counterparts.

class A {
   //Internal class
    fileprivate func hey() {
            print("Hey")
    }
    
    internal func hello() {
        print("Hello")
    }
}

fileprivate class B : A {
    override public func hey() {
        print("B")
    }
    override fileprivate func hello() {
        print("B")
    }   
}
Rule 3: A private function cannot be overridden. An overridden function cannot be private.

class A {
   //Internal class
    fileprivate func hey() {
            print("Hey")
    }
    
    internal func hello() {
        print("Hello")
    }
}

fileprivate class B : A {
    override open func hey() {
        print("B")
    }
    override private func hello() { //compiler error.
        print("B")
    }
}

Swift Access Control – Tuples

The access level of a Swift Tuple would be the level of the most restrictive element among the elements present.


fileprivate class F1{
    
    var str : String
    var number: Int
    init(s: String, n: Int) {
        str = s
        number = n
    }
}
private class F2{
    
    var str : String
    var number: Int
    init(s: String, n: Int) {
        str = s
        number = n
    }
}
class G{
    //compiler error below.
    var tuple : (F1,F2) =  (F1(s: "Anupam",n: 101), F2(s: "JournalDev.com",n: 102))
}

In the above code, the access level of the tuple must be set to private, the more restrictive of the two levels.


private var tuple : (F1,F2) =  (F1(s: "Anupam",n: 101), F2(s: "JournalDev.com",n: 102))

Swift Access Control with Function Types

Functions in swift, like Tuples, have the access level equal to the most restrictive level among the parameters and the return type. But you have to specify the access level of the functions explicitly too.
If the specified access level does not match the calculated one, it’ll throw a compiler error.
The below code looks fine but it would NOT compile:


fileprivate class F1{
    
    var str : String
    var number: Int
    init(s: String, n: Int) {
        str = s
        number = n
    }
}
class G{
    func hey(s: String,y: Int) -> F1 //compiler error.
    {
        print("\(s) and \(y)")
        return F1(s: s, n: y)
    }
}

The return type of hey() is F1 which is a fileprivate class. Hence you must make the function fileprivate.
swift access control functions

Swift Access Control with init function

Swift Initialisers must have the same or lesser access level than its parameters defined.
The following code defines an initaliser in class G that would fail since it has higher access level.


fileprivate class F1{
    
    var str : String
    var number: Int
    init(s: String, n: Int) {
        str = s
        number = n
    }
}
private class F2{
    
    var str : String
    var number: Int
    init(s: String, n: Int) {
        str = s
        number = n
    }
}
class G{
    private var tuple : (F1,F2)
    init(f1: F1, f2: F2) { //compiler error.
        tuple = (F1(s: "Anupam",n: 101), F2(s: "JournalDev.com",n: 102))
    }
}

We can set the initialiser above to either of the fileprivate or private.

Required initialisers follow the same access rule as initialisers along with the rule that the access level should be the same as that of the enclosed class.

So just keeping the fileprivate level on the required init would give the following error.
swift access control initializer

So either make the parameters as the same type of the class OR vice-versa. Here we’ll do the later and make the class as fileprivate.
Continuing with our previous code, using required init would make the class G look like this:


fileprivate class G{
    private var tuple : (F1,F2)
    required fileprivate init(f1: F1, f2: F2) {
        tuple = (F1(s: "Anupam",n: 101), F2(s: "JournalDev.com",n: 102))
    }
}

Default Memberwise Initializers for Structure Types

For structure initialiser we must set the initialiser to the following level based on the members

  • If any of the initialiser members is private or fileprivate the init should be fileprivate
  • Else the init by default is internal.

Continuing with our previous code of fileprivate F1 and private F2 classes, the following code snippet is for the structure’s default initialiser.


struct StructureExample {
private var tuple : (F1,F2)

    fileprivate init(f1: F1, f2: F2) {
        tuple = (F1(s: "Anupam",n: 101), F2(s: "JournalDev.com",n: 102))
    }
}

Recap: The tuple must be set to an access level lower or equal to its elements levels

Swift Access Control Example – Protocols

Protocol access level must be set at the time of defining that protocol.
The protocol functions/requirements must have the same access level defined.
Just like Subclassing, if a protocol inherits from another protocol, it cannot have a higher access level that the inherited one.


private protocol ProtocolA{
    //Your code
}

private protocol ProtocolB : ProtocolA{
    //Your code
}

You cannot set ProtocolB to any higher level such as fileprivate.

Protocol Conformance

  • A class can conform to a protocol with a lower access level.
  • If a class is public and a protocol is private then the type conformance is private.
  • It always the lower one that’s considered.
  • If the protocol methods are implemented in the class/structure/enum, they must be set to an access level that is equal to the protocol access level or higher than that.

Swift Access Control – Enumerations

The access level for an enumeration would be applied across all its cases


private enum Color{
    case red
    case blue
}

red and blue have the same access type private

The access level of the raw values should be greater than or equal to the enum access level.

Swift Access Control – Extensions

Any type members added in an extension must have the same default access level as type members declared in the original type being extended.
To change the access level to a different one from the class, you just need to add the access modifier on the extension and it’ll change the default access levels of the extensions members too unless you set the levels on members explicitly.

You cannot declare a private/fileprivate class with an extension of public or internal or open


private class H{
    private var hello = "Hi"
}
//compiler error below
public extension H{
    var newHello:String  {return "Yeah"}
}

We can access private members from a class/struct in their extensions.
We can access private members from one extension in another.

Swift Access Control – Generics and Type Aliases

  • The access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any of its members/return type.
  • A type alias can have an access level less than or equal to the access level of the type it aliases.
    This is the same as subclassing. Type alias cannot use a higher access level than it’s type.

Comments

  1. FC KATOCH says:

    Sir,, Do you have any book on Swift written by You? or Please just suggest me good books on Swift.
    Thanks!!

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages