Basics
-
Access control is about controlling access to the entities in your code by other code.
-
By default Swift sets the appropriate default access level for various entities.
-
However Swift also allows developer to explicitly specify access level for any entity.
-
The boundaries of access are defined by
-
The type declaration (class, struct etc)
-
The source file (the physical file containing the source)
-
The module (the library or application built and shipped as a single unit)
- Access to any entity inside these boundaries by a code outside the boundary will be restricted by the access levels allowed implicitly/explicitly by that entity or the type declaration enclosing that entity.
Access Levels
Open Access
-
Allows access to the entity from anywhere within the project.
-
Applies only to classes and class members.
-
Open classes can be subclassed in any module.
-
Open class members can be overridden by any subclass in any module.
-
Syntax example
open class MyClass {
open var myVar: Int = 0
}
Public Access
-
Allows access to the entity from anywhere within the project.
-
Public classes can be subclassed only in the defining module.
-
Public class members can be overridden in the subclasses.
-
Syntax example
public class MyClass {}
public var myVar: Int
Internal Access
internal class MyClass {}
internal var myVar: Int
File-Private Access
fileprivate class MyClass {}
fileprivate var myVar: Int
Private Access
private class MyClass {}
private var myVar: Int
Determination of access levels
Contextual default access level
-
-
All entities in the code without an explicit access level get a default access level automatically assigned to them.
-
Default access level for most entities is internal.
-
Default access level for entities inside a Public/Internal Type is internal.
-
Default access level for entities inside a File-Private/Private Type is File-Private/Private respectively.
Dependent maximum access level
-
Any entity defined in terms of other entities has a maximum allowed access level, which is the lowest among those other entities.
-
Basically no entity can have a higher access level than that of the entities it depends up on. For example you can not define a public variable belonging to a private type or a public function accepting or returning a private type.
-
If the contextual default access level is not adequate or is higher than the dependent maximum access level, then you must explicitly specify an appropriate lower access level.
Constants, Variables, Properties
fileprivate struct Test {} // Test is a fileprivate struct
fileprivate let myVar = Test() // Hence myVar must be fileprivate/private
Type Aliases
fileprivate struct Test {} // Test is a fileprivate struct
fileprivate typealias Temp = Test // Hence Temp must be fileprivate/private
Functions and Methods
fileprivate struct Test {} // Test is a fileprivate struct
fileprivate func temp(value: Test) -> Int { // Hence temp should be fileprivate/private
return 0
}
Initialiazers
-
They can not have an access level higher than that of their parameter types.
-
Required initializers should have the same access level as the class they belong to.
-
Default memberwise initializer in a struct can not have an access level higher than that of any of the stored properties.
Tuples
Enums
-
Access level for the cases of an enum is exactly the same as that of the enum. It is not allowed to explicitly specify the access levels for the cases.
-
If raw/associative values are used, then the enum can not have an access level higher than that of the types of the raw/associative values.
fileprivate typealias Value = Int // Value is a fileprivate alias for Int
fileprivate enum Arrows: Value { // Hence Arrows must be fileprivate/private
case UP
case DOWN
case LEFT
case RIGHT
}
Subclasses
class Parent {
func hello() {} // Method hello() is internal in Parent class
}
class Child: Parent {
override public func hello() {} // Method hello() is public in Child class
}
Subscripts
fileprivate typealias Index = Int // Index is a fileprivate alias
struct Test {
fileprivate subscript(index: Index) -> Int { // Hence subscript must be fileprivate/private
return 0
}
}
Getters and Setters
-
Access level for Getters is always the same as that of the constant, variable, property or subscript they belong to.
-
Default access level for Setters also is same as that of the corresponding constant, variable, property or subscript.
-
However Setters can be given a lower access level than the Getter, to better control the read-write scope of the target entity.
-
Above statements are true in the case of both stored and computed properties.
struct Score {
var user: String // Getter and Setter are internal
private var role: String // Getter and Setter are private
public private(set) var value: Int // Getter is public. Setter is private.
private(set) var level: Int // Getter is internal. Setter is private.
}
Protocols
-
Each protocol requirement automatically gets the same access level as that of the protocol itself. There is no provision to set individual access levels for each protocol requirement.
-
A protocol inheriting from another protocol can not have a higher access level than the parent protocol.
-
A type adopting a protocol can have any access level irrespective of the access level of that protocol.
-
The type members implementing the protocol requirements must have an access level at least as high as the minimum access level between the type and the protocol.
// Type member (for protocol) access level >= Minimum access level(Protocol, Type)
public protocol MyProtocol {
var myVar: Int {get set} // myVar requirement has public access level
}
open class MyClass : MyProtocol {
public var myVar: Int = 0 // myVar must have access level >= public
} // which is the minimum of (public, open)
Extensions
-
Contextual default access level for an extension is the same as that of the original type it extends.
-
They can not have a higher access level than that of the original type.
-
Unless explicitly specified, the extension members get contextual default access level exactly in the same manner as the original type members.
-
An extension that declare protocol conformance can not specify an explicit access level. Extension members implementing the protocol requirements must have an access level at least as high as the minimum access level between the type and the protocol.
public class MyClass {}
fileprivate protocol MyProtocol {
var myVar: Int {get}
}
extension MyClass : MyProtocol {
fileprivate var myVar: Int { // myVar should have access level >= fileprivate
get { return 0 } // which is the minimum of (public, fileprivate)
}
}
Generics
fileprivate protocol MyProtocol {}
internal class MyClass {}
public class ReturnClass {}
// myFunction must have access level <= fileprivate
// which is the lowest among the types that it is depending upon
fileprivate func myFunction<FirstType: MyProtocol, SecondType>
(firstVar: FirstType, secondVar: SecondType, thirdVar: MyClass) -> ReturnClass
where SecondType: Equatable {
return ReturnClass()
}
fileprivate protocol MyProtocol {}
// MyClass must have access level <= fileprivate
// which is the lowest among the types that it is depending upon
fileprivate class MyClass <T: MyProtocol> where T: Equatable {}
Unit Testing
-
A unit test module can access any internal entity from the code module, provided the import statements in the unit test module for this code module are marked as @testable, and also that the code module is compiled with testing enabled.
// In testcase files in the unit test module
@testable import MyCodeModule