Swift - Access Control
Published: 2022-04-08 09:01:15

## Access Control

Access control restricts access to parts of your code from code in other source files and modules.

- Specific access levels can be assigned 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

- A **module** is a single unit of code distribution—a framework or application that’s built and shipped as a single unit and that can be imported by another module with Swift’s **import** keyword.

- A **source file** is a single Swift source code file within a module.

---
### Access Level

There are 5 different access level for entities in swift.

  1. Open access 
  2. Public access
  3. Internal access 
  4. File-private access
  5. Private access
 
- **Open access** and **public access** enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. 

- Open access applies only to classes and class members, and it differs from public access by allowing code outside the module to **subclass** and **override**.

- **Internal access** enables entities to be used within any source file from their defining module, but not in any source file outside of that module.

- **File-private access** restricts the use of an entity to its own defining source file.

- **Private access** restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file.

- Open access is the highest (least restrictive) access level and private access is the lowest (most restrictive) access level.

---
### Principle of Access Levels

**_No entity can be defined in terms of another entity that has a lower (more restrictive) access level._**

- A public variable can’t be defined as having an internal, file-private, or private type, because the type might not be available everywhere that the public variable is used.

``` swift
struct someType {   //implicitly declared as internal
    var someInt : Int
}

public var instance = someType(someInt: 5) // Error : Variable cannot be declared public because its type 'someType' uses an internal type
```
-  function can’t have a higher access level than its parameter types and return type, because the function could be used in situations where its constituent types are unavailable to the surrounding code.

---
### Default Access Levels
All entities in your code (with a few specific exceptions) have a default access level of **internal** if you don’t specify an explicit access level.

For **Custom Type** :

- The access control level of a type also affects the default access level of that type’s members (its properties, methods, initializers, and subscripts).

- If a type has access level as private or file private, the default access level of its members will also be **private** or **file private** respectively.

- If you define a type’s acces level as internal or public, the default access level of the type’s members will be **internal**.

- A public type defaults to having internal members (but can be explicitly set to public), not public members to avoid the internal workings of a type as public API by mistake.

example :

``` swift
public class SomePublicClass {                  // explicitly public
    public var somePublicProperty = 0            // explicitly public
    var someInternalProperty = 0                 // implicitly internal
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private
    private func somePrivateMethod() {}          // explicitly private
}

class SomeInternalClass {                       // implicitly internal
    var someInternalProperty = 0                 // implicitly internal
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private
    private func somePrivateMethod() {}          // explicitly private
}

fileprivate class SomeFilePrivateClass {        // explicitly file-private
    func someFilePrivateMethod() {}              // implicitly file-private
    private func somePrivateMethod() {}          // explicitly private
}

private class SomePrivateClass {                // explicitly private
    func somePrivateMethod() {}                  // implicitly private
```

---
### Tuple Types
The access level for a tuple type is the **most restrictive access level** of all types used in that tuple.
example :

``` swift
fileprivate let object1 = (SomeFilePrivateClass(), SomePrivateClass())  // OK
internal let object2 = (SomeFilePrivateClass(), SomePrivateClass())  // Error : Constant cannot be declared internal because its type '(SomeFilePrivateClass, SomePrivateClass)' uses a private type
```

---

### Function Types

The access level for a function type is calculated as the **most restrictive access level** of the function’s parameter types and return type.

example :

``` swift
func someFunction() -> (SomeInternalClass, SomePrivateClass) {  //Error : Function must be declared private or fileprivate because its result uses a private type
    // function implementation goes here
}

private func someOtherFunction() -> (SomeInternalClass, SomePrivateClass) {  // OK
    // function implementation goes here
}
```
---
### Enumeration Types
The individual cases of an enumeration automatically receive the **same access** level as the enumeration they belong to. Different access level can't be specified for individual enumeration cases.

- **Raw Values and Associated Values** : The types used for any raw values or associated values in an enumeration definition must have an access level **at least as high as** the enumeration’s access level.

example :

``` swift
struct UPC {  // implicitly internal
    let numberSystem : Int
    let manufacturer : Int
    let product : Int
    let check : Int
    
    init(_ numberSystem : Int, _ manufacturer: Int, _ product: Int, _ check: Int){
        self.numberSystem = numberSystem
        self.manufacturer = manufacturer
        self.product = product
        self.check = check
    }
}

fileprivate struct QRcode{  // explicitly fileprivate
    let productCode : String
    
    init( _ productCode : String ){
        self.productCode = productCode
    }
}

enum Barcode {  // implicitly internal
    case upc(UPC)
    case qrCode(QRcode)  // Error : Enum case in an internal enum uses a fileprivate type
}
```

---

### Nested Types 

1. The access level of a nested type is the same as its containing type, unless the containing type is public. 
2. Nested types defined within a public type have an automatic access level of internal(but can be explicitly changed).
 
---

### Subclassing
- Subclass can be created for any class that can be accessed in the current access context and that’s defined in the **same module** as the subclass.
- subclass can be created for any **open class** that’s defined in a different module. 
- A subclass can’t have a higher access level than its superclass.
-  For classes that are defined in the same module, class member that’s visible in a certain access context can be overidden. For classes that are defined in another module,  any open class member can overriden.
- An override can make an **inherited class** member **more accessible** than its superclass version.
- It’s even valid for a subclass member to call a superclass member that has lower access permissions than the subclass member, as long as the call to the superclass’s member takes place within an allowed access level context

example :

``` swift
public class A {
    fileprivate func someMethod() {}
}

internal class B: A {
    override internal func someMethod() {
        super.someMethod()
    }
}
```
---

### Constants, Variables, Properties, and Subscripts

- A constant, variable, or property **can’t be more public** than its type. It’s not valid to write a public property with a private type.

example :

``` swift
private var privateInstance = SomePrivateClass()
```
- **Getters** and **setters** for constants, variables, properties, and subscripts automatically **receive the same access level** as the constant, variable, property, or subscript they belong to.

- setter can have a lower access level than its corresponding getter, to restrict the read-write scope of that variable, property, or subscript. Like fileprivate(set), private(set), or internal(set) can be prefixed before the var or subscript.

- This rule applies to stored properties as well as computed properties. Even though you don’t write an explicit getter and setter for a stored property, Swift still synthesizes an implicit getter and setter for you to provide access to the stored property’s

example :

``` swift
public struct TrackedString {
    public private(set) var numberOfEdits = 0 // public getter and private setter
    public var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
    public init() {}
}
```

---

### Initializers

- Custom initializers can be assigned an **access level less than or equal** to the type that they initialize.

- A required initializer must have the **same access level** as the class it belongs to.

- The types of an initializer’s parameters **can’t be more private** than the initializer’s own access level.

- A default initializer has the **same access level** as the type it initializes, unless that type is defined as public. For a type that’s defined as public, the default initializer is considered **internal** (public no-argument initializer can be provided explicitly).

- The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are **private**. Likewise, if any of the structure’s stored properties are **file private**, the initializer is file private. Otherwise, the initializer has an access level of **internal**.

---

### Protocols
- The access level of each requirement within a protocol definition is automatically set to the **same access level** as the protocol. 

- A protocol's requirement **can't be changed** to a different access level than the protocol it supports.

- For a public protocol, the protocol’s requirements require a public access level for those requirements when they’re implemented(not Internal like in other places). 

- The Inherited protocol can have **at most the same access level** as the protocol it inherits from.

- A type can conform to a protocol with a **lower access level** than the type itself. For a public type that can be used in other modules, but whose conformance to an internal protocol can only be used within the internal protocol’s defining module.

- The context in which a type conforms to a particular protocol **is the minimum of the type’s access level and the protocol’s access level**. For example, if a type is public, but a protocol it conforms to is internal, the type’s conformance to that protocol is also internal.

- When you write or extend a type to conform to a protocol, you must ensure that the type’s implementation of each protocol requirement has at **least the same access level** as the type’s conformance to that protocol. For example, if a public type conforms to an internal protocol, the type’s implementation of each protocol requirement must be at least internal.

example :

``` swift
public protocol SomeProtocol{
    func someMethod() -> Int
    func someOtherMethod()-> Int
}

class SomeClass : SomeProtocol{
    private func someMethod() -> Int { return 5 } // Error : Method 'someMethod()' must be as accessible as its enclosing type because it matches a requirement in protocol 'SomeProtocol'
    func someOtherMethod() -> Int { return 10 }  // OK , implicitly internal
}
```

--- 
###Extension

 A class, structure, or enumeration can be extended in any access context in which the class, structure, or enumeration is available.

- Any type members added in an extension have the **same default access level** as type members declared in the original type being extended.

If extended type is 

  1. public or internal type, any new type members you add have a default access level of **internal**.  
  2. file-private type, any new type members you add have a default access level of **file private**.  
  3. private type, any new type members you add have a default access level of **private**.

- Alternatively, you can mark an extension with an explicit access-level modifier to set a new default access level for all members defined within the extension. This new default can still be overridden within the extension for individual type members.

- Explicit access-level modifier can't be used for an extension if that extension is used to add protocol conformance.

- **Private Members in Extensions**
Extensions that are in the same file as the class, structure, or enumeration that they extend behave as if the code in the extension had been written as part of the original type’s declaration. Below declarations are correct

  1. Declare a private member in the original declaration, and access that member from extensions in the same file.
  2. Declare a private member in one extension, and access that member from another extension in the same file.
  3. Declare a private member in an extension, and access that member from the original declaration in the same file.

example :

``` swift
protocol SomeProtocol {
    func doSomething()
}

struct SomeStruct {
    private var privateVariable = 12
}

extension SomeStruct: SomeProtocol {
    func doSomething() {
        print(privateVariable)
    }
}
```
---
### Generics

The access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any type constraints on its type parameters.

---
### Type Aliases

- Any declared type aliases are treated as **distinct types** for the purposes of access control. 
- A type alias can have an access level **less than or equal to** the access level of the type it aliases. For example, a private type alias can alias a private, file-private, internal, public, or open type, but a public type alias can’t alias an internal, file-private, or private type.
- This rule also applies to type aliases for **associated types** used to satisfy protocol conformances.