Access Control Basics in iOS Swift — Part 2

A quick demo of Higher levels of access control in Swift

Richard Lu
6 min readSep 16, 2019
Great illustration by drawkit.io

This is the second part of my series of Access Control Basics in Swift, to help you have a better understanding of this part, check out my previous Access Control Basics in Swift — Part 1 for some intro.

In this part, we will talk about the last 2 highest levels of access control, public and open. They have a lot in common but, of course, there are some critical differences between them. We will first talk about what are those common restrictions they share.

Entities with open access or public access can not only be used in source files from their defining modules, but also from other modules that import their defining modules. I like to put it this way: Open access and public access are cross-modules-wise. (In part 1, I also describe private access and fileprivate access are source-file-wise and internal access is single-module-wise, these are just my own words.)

In order to demonstrate how to use entities with public access and open access across modules, we need to create another module other than working-on target in our project. Framework is a great choice for this demo. We can create a framework in our project, which is pretty easy by following steps below (please check caption of each image):

Create a new Xcode project named AccessControlDemo(or any personal reference) and select “+” at the bottom of target list to add a new framework then hit “Next”.
Name the new framework RegularPolygon and we will use this framework to create and deal with some regular polygons.
After the RegularPolygon framework is created successfully, we can see the new folders been added in the Navigator list.
Under the newly added RegularPolygon folder, we create a new swift file named RegularPolygon.

In the RegularPolygon.swift file, we are going to define a RegularPoly class with open access as below

open class RegularPolygon {
private let numberOfSides: UInt
private let lengthOfSide: UInt

public init(n: UInt, l: UInt) {
self.numberOfSides = n
self.lengthOfSide = l
}
public func getPerimeter() -> UInt{
//Calculate polygon perimeter
return numberOfSides*lengthOfSide
}
open func description() -> String {
// A brief description of regular polygon
return "A \(numberOfSides)-side regular polygon"
}
}

In the defining disclosure of class RegularPolygon, we have two private constants, numberOfSides and lengthOfSide, which are only for perimeter calculation in a public function getPerimeter(). (Anther function description() is specified with open access, this is for later use for demonstrating differences between open access and public access.)

Those two non-optional constants need to be initialized in a designated initializer. In order to enable access while creating an instance of RegularPolygon class in other modules, we provided public access to the initializer, otherwise, it will be internal access by default and we will see a run time error saying ’RegularPolygon’ initializer is inaccessible due to ‘internal’ protection level. (Initializer cannot be declared open, or there will be an error saying Only classes and overridable class members can be declared ‘open’; use ‘public’.)

This RegularPolygon framework is actually ready for use now. We can switch to default Viewcontroller.swift file under our original ControlAccessDemo target(module) and import RegularPolygon. Then we create a regular polygon instance in viewDidLoad() function.

import UIKit
import RegularPolygon
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.

let fourSideRegularPolygon = RegularPolygon(n: 4, l: 2)
print("Perimeter = \(fourSideRegularPolygon.getPerimeter())")
// prints "Perimeter = 8"
print(fourSideRegularPolygon.description())
// prints "A 4-side regular polygon"
}
}

Here we demonstrate that open access or public access entities can be used in modules that import their defining module.

Now we are going to see what are the difference between open access and public access.

In my own words, they are different from whether classes or class members can be subclassed or overridden in subclass within any other modules that import their defining module.

Words in The Swift Programming Language(Swift5.0) are also very straight forward. They help me to have a much better understanding while reading, thus I decide to quote the content directly below as section headers. (Contents have been reordered in the sequence of my demonstration steps, however, this should not affect how we learn about this)

Open classes can be subclassed within the module where they’re defined, and within any module that imports the module where they’re defined.

So far we have an open class named RegularPolygon and now we are going to define a new public class in the RegularPolygon framework, which will be a subclass of RegularPolygon class.

Let’s add a new .swift file under RegularPolygon framework and name it Square. Now define a public class named Square with a custom designated initializer, init(sideLength: UInt), which only takes one sideLength parameter since the square is always with 4 sides.

public class Square: RegularPolygon {
public init(sideLength: UInt) {
// Set numberOfSides equals to 4
// Pass down the sideLength parameter
super.init(n: 4, l: sideLength)
}
}

Open class members can be overridden by subclasses within the module where they’re defined, and within any module that imports the module where they’re defined.

Now we are going to override the open func description() -> String from Square’s superclass, RegularPolygon. the description() function in Square is marked with override and with public access (Overriding instance method has to be as accessible as its enclosing type, which in our case is open access or public access)

public class Square: RegularPolygon {
public init(sideLength: UInt) {...}
override public func description() -> String {
// Replacing original description to a specific name for 4-sided
// regular polygon: Square.
return "A square"
}
}

Everything looks great now👍. We can now go ahead and try to create an instance of this public Square class in ViewController.swift file’s viewDidLoad() function.

import UIKit
import RegularPolygon
class ViewController: UIViewController {
override func viewDidLoad() {
...

let sq = Square(sideLength: 5)
let perimeter = sq.getPerimeter()
print("Perimeter of square = \(perimeter)")
// prints "Perimeter = 20"
print(sq.description())
// prints "A square"

}
}

Classes with public access, or any more restrictive access level, can be subclassed only within the module where they’re defined.

If we want to have a different kind of square, let’s say, a colored square. We can define a new class called ColorSquare. Since square class is with public access, we can only define a subclass of Square class within our RegularPolygon framework(module).

We can now create a new .swift file named ColorSquare under RegularPolygon framework and write down the definition of ColorSquare class.

We want this ColorSquare can also be used outside of its defining module, thus we define it as a public class with public initializer.

public class ColorSquare: Square {
var colorName: String
public init(colorName: String, sideLength: UInt) {
self.colorName = colorName
super.init(sideLength: sideLength)
}
override public func description() -> String {
return "A \(colorName) square"
}
}

Class members with public access, or any more restrictive access level, can be overridden by subclasses only within the module where they’re defined.

Here we are going to override the description() function again in order to provide square’s color info in it.

public class ColorSquare: Square {
...
override public func description() -> String {
return "A \(colorName) square"
}

}

Now the ColorSquare class is all set. To create a ColorSquare instance, we insert new lines of codes as shown below.

import UIKit
import RegularPolygon
class ViewController: UIViewController {
override func viewDidLoad() {
...

let csq = ColorSquare(colorName: "blue", sideLength: 5)
let perimeter = csq.getPerimeter()
print("Perimeter of square = \(perimeter)")
// prints "Perimeter = 20"
print(sq.description())
// prints "A blue square"

}
}

Congrats! we finish all the examples. 🎉🎉🎉

I do really hope that this series can help you get a better understanding of some basics of Access Control. I am also helping myself to solidify my knowledge in this topic while writing the series. I’ve been taking a lot from all kinds of resources online but seldom giving back. That’s why I decided to start writing. Your response or applause will definitely encourage me to keep doing this. Give and take makes good friends. 😊😊😊 See you next time.

--

--

Richard Lu

🌞iOS Developer | 🌛2 kids' Daddy | Striving for being awesome day and night