Access Control by Example - Swift Programming Language

  • Access control restricts access to parts of your code from code in other source files and modules.
  • This feature enables you to hide the implementation details of your code, and to specify a preferred interface through which that code can be accessed and used.
  • You can assign specific access levels to individual types (classes, structures, and enumerations), as well as to properties, methods, initializers, and subscripts belonging to those types. Protocols can be restricted to a certain context, as can global constants, variables, and functions.

Modules and Source Files

  • Swift’s access control model is based on the concept of 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.
  • Each build target (such as an app bundle or framework) in Xcode is treated as a separate module in Swift.
  • If you group together aspects of your app’s code as a stand-alone framework—perhaps to encapsulate and reuse that code across multiple applications—then everything you define within that framework will be part of a separate module when it’s imported and used within an app, or when it’s used within another framework.

Access Levels

  • Swift provides five different access levels for entities within your code.
  • These access levels are relative to the source file in which an entity is defined, and also relative to the module that source file belongs to.

1. open  — least restrictive

  • Enable an entity to be used outside the defining module (target). You typically use open or public access when specifying the public interface to a framework.

  • Let’s consider an example: UIKit. We often use many UI elements from UIKit daily. When you try to use the UIComponents such as UIButton, UICollectionView, UITableView, UIViewController etc.., you have to import the UIKit on the top. So UIKit is actually a module that we can import to our class and subclass its features.

import UIKit

class BobbyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("hello")
    }
}

let obj = BobbyViewController()
obj.viewDidLoad()  

/* prints 
hello */
  • viewDidLoad is a function that is marked with open. You even have the ability to override it.

2. public

  • Like open access level, public access level enable an entity to be used outside the defining module (target). But open access level allows us to subclass it from another module where in public access level, we can only subclass or overridde it from within the module it is defined.
// RandomLibraryFromGithub
public func A() {}
open func B() {}

// In your current project
override func A(){} // error
override func B(){} // success

3. internal

  • internal is the default access level.
  • Internal classes and members can be accessed anywhere within the same module(target) they are defined.
  • You typically use internalaccess when defining an app’s or a framework’s internal structure.
class Test1{
    internal var a = 30
    var b = 50
    init() {
        print("value of a and b",a,b)
    }
}

class Test2: Test1{
    let obj1 = Test1()
//  Uncomment next lines and it will show error unauthorized access
//    obj1.a = 40
//    obj1.b = 90
}
let obj2 = Test2()

/* reults:
value of a and b 30 50
value of a and b 30 50
*/

4. fileprivate

  • Restricts the use of an entity to its defining source file.
  • You typically use fileprivate access to hide the implementation details of a specific piece of functionality when those details are used within an entire file. ie; the functionality defined with a fileprivate access level can only be accessed from within the swift file where it is defined.

// NewViewController.swift
fileprivate class NewViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
}

// ViewController.swift
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // uncommenting next line will show an error as class is defined as fileprivate
        // let newViewController = NewViewController()
        let bobbyViewController = BobbyViewController()
        bobbyViewController.viewDidLoad()
    }
}
let viewController = ViewController()
viewController.viewDidLoad()

/* prints:
hello */

5. private — most restrictive

  • Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file.
  • You typically use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
class Game {
    private var number = 0
    
    func score() {
        print(number)
    }
    
    func increaseNumberByOne() {
        number += 1
    }
}
//uncommenting next line will show an error of inaccesibility
//Game().number
Game().score()

//prints: 0
  • Before Swift 4, it was not able to use anything that has to do with private even with extension. However, it is possible in Swift 4.
class MyClass: UIViewController{
    private var myClassVarA = 90
    override func viewDidLoad() {
        super.viewDidLoad()
        print("private variable:",myClassVarA)
    }
}
extension MyClass{
    func printValueOfA(){
        print("accessing private variable of same class in extension:",myClassVarA)
    }
}
let myClassObj = MyClass()
myClassObj.viewDidLoad()
myClassObj.printValueOfA()

/* prints:
private variable: 90
accessing private variable of same class in extension: 90 */

Next - Enum (Enumerations) by Example


You can download the swift playground of all above examples from Here




Related Article