Swift - Access Control



Access controls are used to restrict access to the specified part of the code or module. It prevents unauthorized access and implements encapsulation. The access control mechanism controls the access of the methods and properties of classes, structures and enumerations.

Constants, variables and functions in a protocol are restricted and allowed access as global and local through access control. Access control applied to properties, types and functions can be referred to as 'entities'.

The access control model is based on modules and source files. The module is defined as a single code distribution unit and can be imported using the keyword 'import'. A source file is defined as a single source code file within a module to access multiple types and functions.

Access Levels in Swift

Swift supports the following access levels and they are:

Access Levels Definition
Open It is the least restrictive access level. It is applicable only to classes and class memebers. It allows class and class members to be subclassed or overrridden in another module.
Public It allows entities to be access from any source file within the same or different module.
Internal It allows entities to be used within any source file from their defining module, but not outside of that module.
Private It restricts the use of an entity to its own defining source file. It hides the implementation details of a specific code functionality. It is the most restrctive access level.
File-Private It also restricts the use of an entity to its own defining source file. It hides the implementation details of a specific code functionality when those details are used inside the entire file.

Rules of using Access levels

Following are some rules that must be known by the developers while using access level −

  • The public variable can not be defined as internal, file-private or private.

  • The function does not have a higher access level than its parameter and return type.

  • The default access level for all the entities is internal.

  • The default access level for single-target apps is internal.

  • The default access level for frameworks is either open or public.

Syntax

public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

Access Control for Function types

Some functions may have arguments declared inside the function without any return values. The following program declares a and b as arguments to the sum() function. Inside the function itself, the values for arguments a and b are passed by invoking the function call sum() and its values are printed thereby eliminating return values.

To make the function's return type as private, declare the function's overall access level with the private modifier.

Example

Swift program to demonstrate access control for function types.

// Private function
private func sum(a: Int, b: Int) {
   let a = a + b
   let b = a - b
   print(a, b)
}

// Calling the function
sum(a: 20, b: 10)
sum(a: 40, b: 10)
sum(a: 24, b: 6)

Output

It will produce the following output −

30 20
50 40
30 24

Access Control for Enumeration types

The enumeration in Swift automatically receives the same access level for individual cases of an enumeration. Consider for example accessing the student name and marks secured in three subjects enumeration name is declared as a student and the members present in the enum class are a name that belongs to string datatype, marks are represented as mark1, mark2 and mark3 of datatype Integer.

To access either the student name or marks they have scored. Now, the switch case will print the student's name if that case block is executed otherwise it will print the marks secured by the student.

Example

Swift program to demonstrate access control for enumeration types.

// Enumeration
public enum Student {
   case Name(String)
   case Mark(Int, Int, Int)
}

var studDetails = Student.Name("Swift")
var studMarks = Student.Mark(98, 97, 95)

switch studDetails {
   case .Name(let studName):
      print("Student name is: \(studName).")
   case .Mark(let Mark1, let Mark2, let Mark3):
      print("Student Marks are: \(Mark1), \(Mark2), \(Mark3).")
}

Output

It will produce the following output −

Student name is: Swift.

Access Control for SubClasses

Swift allows the user to subclass any class that can be accessed in the current access context. A subclass cannot have a higher access level than its superclass. The user is restricted from writing a public subclass of an internal superclass.

Example

Swift program to demonstrate access control for subclasses.

// Class
public class Cricket {
   internal func display() {
      print("Welcome to Swift Super Class")
   }
}

// Subclass
internal class Tennis: Cricket {
   override internal func display() {
      print("Welcome to Swift Sub Class")
   }
}

let cricketInstance = Cricket()
cricketInstance.display()

let tennisInstance = Tennis()
tennisInstance.display()

Output

It will produce the following output −

Welcome to Swift Super Class
Welcome to Swift Sub Class

Access Control for Constants, variables, properties and subscripts

We are allowed to control the visibility of the constants, variables, properties and subscripts with the help of access controls. When a constant, variable, property or subscript uses private type, then that constant, variable, property, or subscript must be marked as private.

And we are not valid to write a public property with a private type. Similarly, a subscript cannot be more public than its return type.

Example

private var privateInstance = SomePrivateClass()

Access Control for Getters and Setters

We can set the access control for the getter and setter independently to control their visibility. The access level of getters and setters is determined by the access level of the constant, variable, property, or subscript to which they belong.

Example

Swift program to demonstrate access control for getters and setters.

class Samplepgm {
   private var counter: Int = 0{
      willSet(newTotal){
         print("Total Counter is: \(newTotal)")
      }
      didSet{
         if counter > oldValue {
            print("Newly Added Counter \(counter - oldValue)")
         }
      }
   }
}

let NewCounter = Samplepgm()
NewCounter.counter = 100

Output

It will produce the following output −

Total Counter is: 100
Newly Added Counter 100
Total Counter is: 800
Newly Added Counter 700

Access Control for Initializers and Default Initializers

In Swift, we are allowed to specify the access controls for the initializer. For custom initializers, we can assign an access level less than or equal to the type that they initialize. A required initializer must have the same access level as the class it belongs to.

Whereas a default initializer has the same access level as the type it initializes unless that type is defined as public. When a default initialize is defined as public it is considered as internal. If we require a public type to be initializable with a no-argument initializer in another module, provide explicitly a public no-argument initializer as part of the type's definition.

Also, the parameters of the initializers cannot be more private than the initializer's own access level. To declare every subclass of the initialize 'required' keyword needs to be defined before the init() function.

Example

Swift program to demonstrate access control for initializers.

class ClassA {
   required init() {
      let a = 10
      print(a)
   }
}
class ClassB: ClassA {
   required init() {
      let b = 30
      print(b)
   }
}
let instanceA = ClassA()
let instanceB = ClassB()

Output

It will produce the following output −

10
30
10

Access Control for Protocols

When we define a new protocol to inherit functionalities from an existing protocol, both have to declare the same access levels to inherit the properties of each other. Swift access control won’t allow the users to define a 'public' protocol that inherits from an 'internal' protocol.

Example

Swift program to demonstrate access control for protocols.

// Public protocol
public protocol tcpprotocol {
   init(no1: Int)
}

// Public class
public class mainClass {
   var no1: Int 
   
   // Initializer 
   init(no1: Int) {
      self.no1 = no1 
   }
}

// Class that uses protocol and class
class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }

   // Requires only one parameter for convenient method
   required override convenience init(no1: Int)  {
      self.init(no1:no1, no2:0)
   }
}

let obj1 = mainClass(no1: 20)
let obj2 = subClass(no1: 30, no2: 50)

print("res is: \(obj1.no1)")
print("res is: \(obj2.no1)")
print("res is: \(obj2.no2)")

Output

It will produce the following output −

res is: 20
res is: 30
res is: 50

Access Control for Generics

Generics allow the user to specify minimum access levels to access the type constraints on its type parameters.

Example

Swift program to demonstrate access control for generics.

public struct TOS<T> {
   var items = [T]()
    
   mutating func push(item: T) {
      items.append(item)
   }

   mutating func pop() -> T {
      return items.removeLast()
   }
}

var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)
let deletedTos = tos.pop()

Output

It will produce the following output −

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Access Control for Type Aliases

In Swift, we can define type aliases to treat distinct access control types. The same access level or different access levels can be defined. A type alias can have an access level less than or equal to the access level of the type it aliases.

When a type alias is 'private' its associated members can be declared as 'private, internal of public type'. When the type alias is public the members cannot be aliased as an 'internal' or 'private' name.

Example

Swift program to demonstrate access control for type aliases.

// Defining a type alias with their access control
public typealias alias1 = Int
internal typealias alias2 = Double

// Class using the type aliases
class AliasClass {
   var publicValue: alias1 = 33
   var internalValue: alias2 = 18.22

   func displayData() {
      print("Public Value: \(publicValue)")
      print("Internal Value: \(internalValue)")
   }
}

// Creating instance of class
let obj = AliasClass()
obj.displayData()

Output

It will produce the following output −

Public Value: 33
Internal Value: 18.22
Advertisements