Swift Access Control

Swift-access-control defines the visibility and accessibility of types, properties, methods, and other entities within your codebase. It is a powerful tool for encapsulating implementation details and providing a clear API. By leveraging access control, you can ensure modularity, security, and maintainability in your Swift projects.

Chapter Goals

  • Understand the purpose of access control in Swift.
  • Learn about the different access levels and their applications.
  • Explore syntax for setting access levels for various entities.
  • Implement real-world examples to enforce encapsulation and API design.

Key Characteristics of Swift Access Control

  • Encapsulation: Restricts access to implementation details.
  • Security: Prevents unintended use of sensitive code.
  • Modularity: Facilitates the design of reusable components.
  • Customization: Offers granular control over visibility.

Basic Rules for Access Control

  • Access levels range from the most restrictive (private) to the least restrictive (open).
  • Specify access levels using keywords such as private, fileprivate, internal, public, and open.
  • Default access level for entities is internal.
  • Access control applies to types, properties, methods, initializers, and subscripts.
  • An entity’s access level must be as restrictive as the most restrictive element it references.

Syntax Table

Serial No Feature Syntax/Example Description
1 Declaring Private Access private var propertyName: Type Restricts access to the enclosing declaration.
2 Declaring Fileprivate Access fileprivate func methodName() { … } Restricts access to the same file.
3 Declaring Internal Access internal let constantName: Type Accessible within the same module.
4 Declaring Public Access public class ClassName { … } Accessible from any module, but not subclassable.
5 Declaring Open Access open class ClassName { … } Fully accessible and subclassable from any module.

Syntax Explanation

1. Declaring Private Access

What is Private Access?

Private access restricts the visibility of an entity to the enclosing declaration.

Syntax

private var counter: Int = 0

Detailed Explanation

  • Use private to limit access to the file’s declaration where the entity is defined.
  • Prevents access from extensions or other declarations in the same file.
  • Ideal for encapsulating implementation details within a specific type.

Example

class Counter {

    private var count = 0

 

    func increment() {

        count += 1

    }

 

    func getCount() -> Int {

        return count

    }

}

 

let counter = Counter()

counter.increment()

print(counter.getCount())

Example Explanation

  • Declares a count property as private to prevent external access.
  • Provides controlled access through increment and getCount methods.

2. Declaring Fileprivate Access

What is Fileprivate Access?

Fileprivate access restricts visibility to the same file where the entity is declared.

Syntax

fileprivate func logMessage(_ message: String) {

    print(“Log: \(message)”)

}

Detailed Explanation

  • Use fileprivate for entities that need to be accessed by multiple declarations within the same file.
  • Provides more flexibility than private but ensures encapsulation at the file level.

Example

class Logger {

    fileprivate func log(_ message: String) {

        print(“Log: \(message)”)

    }

}

 

extension Logger {

    func debug(_ message: String) {

        log(“DEBUG: \(message)”)

    }

}

 

let logger = Logger()

logger.debug(“This is a debug message.”)

Example Explanation

  • Declares a log method as fileprivate.
  • Allows access from an extension within the same file.

3. Declaring Internal Access

What is Internal Access?

Internal access allows visibility within the same module.

Syntax

internal class Helper {

    func assist() {

        print(“Assisting…”)

    }

}

Detailed Explanation

  • internal is the default access level in Swift.
  • Enables access within the same app or framework module but not outside.
  • Suitable for code that supports the module’s functionality but isn’t part of its external interface.

Example

internal struct Calculator {

    func add(_ a: Int, _ b: Int) -> Int {

        return a + b

    }

}

 

let calculator = Calculator()

print(calculator.add(2, 3))

Example Explanation

  • Declares a Calculator struct with an internal access level.
  • Ensures visibility within the same module.

4. Declaring Public Access

What is Public Access?

Public access allows visibility from any module while restricting subclassing or overriding.

Syntax

public class Shape {

    public var sides: Int

 

    public init(sides: Int) {

        self.sides = sides

    }

}

Detailed Explanation

  • Use public for entities intended to be part of the module’s public interface.
  • Allows usage in external modules but prevents subclassing or method overriding.
  • Ideal for stable APIs that do not require extensibility.

Example

public struct Point {

    public var x: Double

    public var y: Double

 

    public init(x: Double, y: Double) {

        self.x = x

        self.y = y

    }

}

 

let point = Point(x: 3.0, y: 4.0)

print(point)

Example Explanation

  • Declares a Point struct as public.
  • Allows creation and usage in external modules.

5. Declaring Open Access

What is Open Access?

Open access allows visibility and subclassing or overriding from any module.

Syntax

open class Vehicle {

    open func start() {

        print(“Vehicle started”)

    }

}

Detailed Explanation

  • Use open for entities that require maximum accessibility and extensibility.
  • Open classes and methods can be subclassed and overridden in external modules.
  • Best for frameworks or libraries designed to be extended by other developers.

Example

open class Car {

    open func drive() {

        print(“Driving a car”)

    }

}

 

class SportsCar: Car {

    override func drive() {

        print(“Driving a sports car”)

    }

}

 

let car = SportsCar()

car.drive()

Example Explanation

  • Declares a Car class as open.
  • Subclasses and overrides the drive method in an external module.

Real-Life Project: Bank Account System

Project Goal

Use access control to encapsulate and protect sensitive bank account data.

Code for This Project

class BankAccount {

    private var balance: Double = 0.0




    func deposit(amount: Double) {

        balance += amount

    }




    func withdraw(amount: Double) -> Bool {

        guard balance >= amount else {

            return false

        }

        balance -= amount

        return true

    }




    func getBalance() -> Double {

        return balance

    }

}




let account = BankAccount()

account.deposit(amount: 100.0)

if account.withdraw(amount: 50.0) {

    print("Withdrawal successful. Remaining balance: \(account.getBalance())")

} else {

    print("Insufficient funds.")

}

Steps

  1. Declare a BankAccount class with a private balance property.
  2. Provide controlled access through public methods for depositing, withdrawing, and checking the balance.
  3. Test the system by simulating transactions.

Save and Run

Steps to Save and Run

  1. Write the code in your Swift IDE (e.g., Xcode).
  2. Save the file using Command + S (Mac) or the appropriate save command.
  3. Click “Run” or press Command + R to execute the program.

Benefits

  • Demonstrates encapsulation using access control.
  • Protects sensitive data from unauthorized access.
  • Provides a clear and controlled API for interacting with the bank account.

Best Practices

Why Use Access Control?

  • Enforces encapsulation and modular design.
  • Prevents unintended use of internal implementation details.
  • Facilitates API design with clear and controlled interfaces.

Key Recommendations

  • Use private for sensitive or implementation-specific details.
  • Use fileprivate for entities shared within a single file.
  • Use internal for module-wide visibility in app or framework development.
  • Use public and open judiciously to design stable and extensible APIs.

Example of Best Practices

public class APIManager {

    private var apiKey: String

 

    public init(apiKey: String) {

        self.apiKey = apiKey

    }

 

    public func fetchData() {

        print(“Fetching data with API key: \(apiKey)”)

    }

}

 

let manager = APIManager(apiKey: “123456”)

manager.fetchData()

Insights

Swift access control is essential for designing secure, modular, and maintainable code. By using appropriate access levels, developers can create well-encapsulated and reusable components.

Key Takeaways

  • Access control ensures appropriate visibility for code entities.
  • Use private and fileprivate for internal implementation details.
  • Use public and open for designing external APIs and extensibility.