Learning iOS Development

Understanding Access Levels in Swift with Examples

Access levels are a powerful tool for maintaining code organization and protecting your code’s integrity

Shashank Thakur
Mobile App Development Publication

--

Mastering Access Levels in Swift
Photo by Martin Sanchez on Unsplash

Access control is a vital aspect of Swift, the modern and safety-conscious programming language from Apple. It allows developers to finely manage how different parts of their codebase interact with one another. In Swift, there are five distinct access levels, each serving specific purposes. In this blog, we’ll dive deep into these access levels, understand their nuances, and explore real-world examples of when and how to use them effectively.

The Five Access Levels in Swift

1. Private

  • Use Case: The private access level is for code elements meant to be accessed only within the defining scope, typically within a class or an extension in the same file.
  • Example: Consider private properties or methods within a class that are implementation details, hidden from the outside world.
class BankAccount {
private var balance: Double = 0.0
}

2. File-Private

  • Use Case: fileprivate restricts access to code elements within the same Swift source file. Use it for entities that should remain hidden from code in other files, even within the same module.
  • Example: Think of helper functions or properties that need to stay confined within a single source file.
fileprivate func calculateTax(income: Double) -> Double {
// Tax calculation logic
}

3. Internal

  • Use Case: internal is the default access level in Swift. It allows code elements to be accessed within the same module but not outside of it.
  • Example: Consider using it for classes, functions, or variables that are part of your module’s implementation but not exposed as part of the public interface.
internal struct Constants {
static let defaultThreshold = 0.5
}

4. Public

  • Use Case: The public access level is suitable for code elements that need to be accessible from outside your module. However, it prevents them from being subclassed or overridden outside the module.
  • Example: Think of framework APIs intended for use by other modules but not intended for extension or subclassing by consumers of the framework.
public class NetworkingManager {
public func fetchData() {
// Fetch data from a server
}
}

5. Open

  • Use Case: open is ideal for code elements that should be accessible, subclassed, and overridden both within your module and by external modules.
  • Example: Consider using it when creating base classes or frameworks where you want consumers to have the freedom to extend or customize behavior.
open class Shape {
open func area() -> Double {
return 0.0
}
}

Real-World Usage Scenarios

Understanding access levels becomes more intuitive when you see how they apply in real-world scenarios:

1. Private Access

class UserCredentials {
private var username: String
private var password: String

init(username: String, password: String) {
self.username = username
self.password = password
}

func authenticate() -> Bool {
// Authentication logic
return true
}
}

In this example, the username and password properties are kept private to the UserCredentials class, ensuring that sensitive information remains concealed.

2. File-Private Access

fileprivate struct GeometryUtils {
fileprivate static func calculateArea(length: Double, width: Double) -> Double {
return length * width
}
}

Here, GeometryUtils contains file-private functionality, allowing it to encapsulate geometry calculations within a single source file without exposing them to other parts of the module.

3. Internal Access

internal class InternalLogger {
internal func logMessage(message: String) {
// Logging logic
}
}

InternalLogger demonstrates internal access, making it accessible throughout the module but hidden from external modules. It's a useful utility for logging purposes.

4. Public Access

public struct UtilityLibrary {
public static func doSomethingUseful() {
// Perform useful operations
}
}

In this case, UtilityLibrary is marked as public, allowing other modules to access the doSomethingUseful function, while still preserving control over its implementation within the module.

5. Open Access

open class BaseViewController {
open func viewDidLoad() {
// Base implementation
}
}

BaseViewController is declared open, enabling both module-internal and external code to subclass and extend it. This is commonly seen in frameworks where customization and extension are encouraged.

Choosing the Right Access Level

Selecting the appropriate access level is vital for maintaining code integrity and ensuring your codebase is robust and secure. Here are some guidelines to help you decide:

  1. Private Access: Use private when you need to hide implementation details within a class or an extension, ensuring encapsulation and preventing unintended access.
  2. File-Private Access: Choose fileprivate to restrict access to code elements within a single Swift source file, isolating them from other parts of the module.
  3. Internal Access: The default internal access level is suitable for most code elements. It allows access within the module but not outside it.
  4. Public Access: Opt for public when you want to expose code elements as part of your module's public API, but you don't want them to be extended or subclassed outside the module.
  5. Open Access: Use open when you want to create code elements that can be accessed, subclassed, and overridden both within your module and by external modules.

Conclusion

Remember that access levels are a powerful tool for maintaining code organization and protecting your code’s integrity. Regularly review and adjust access levels as your codebase evolves to ensure a clear and secure architecture.

--

--