Swift Access Control

Access control can restrict the access level of code in other source files or modules to your code.

You can explicitly set access levels for individual types (classes, structures, enumerations), and also set access levels for properties, functions, initializers, basic types, subscripts, etc. of these types.

Protocols can also be restricted to a certain scope, including global constants, variables, and functions in the protocol.

Access control is based on modules and source files.

A module refers to a Framework or Application built and released as an independent unit. In Swift, one module can use the import keyword to import another module.

A source file is a single source code file, which usually belongs to a module. A source file can contain definitions of multiple classes and functions.

Swift provides four different access levels for entities in code:public、internal、fileprivate、private。

Access Level Definition
public Can access any entity in source files within its own module, and others can also access all entities in the source files by importing the module.
internal Can access any entity in source files within its own module, but others cannot access entities in the source files of that module.
fileprivate Private to the file, can only be used in the current source file.
private Can only be accessed within the class; once outside the scope of the class or structure, it cannot be accessed.

public is the highest access level, and private is the lowest access level.

Syntax

Use the modifiers public, internal, fileprivate, and private to declare the access level of an entity:

Example

public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}

Unless otherwise specified, entities use the default access level internal.

Unspecified access level defaults to internal

class SomeInternalClass {} // The access level is internal let someInternalConstant = 0 // The access level is internal

Function Type Access Control

The access level of a function is determined by the access levels of its parameter types and return type.

The following example defines a global function named someFunction without explicitly declaring its access level.

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

One of the classes in the function, SomeInternalClass, has an access level of internal, and another, SomePrivateClass, has an access level of private. Therefore, according to the tuple access level principle, the tuple's access level is private (the tuple's access level is the same as the lowest access level among the types in the tuple).

Because the function's return type has a private access level, you must use the private modifier to explicitly declare the function:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

Declaring the function as public or internal, or using the default access level internal, is wrong, because if you do so you cannot access the private-level return value.


Enumeration Type Access Control

The access level of members in an enumeration is inherited from the enumeration; you cannot individually declare different access levels for members in the enumeration.

Example

For example, in the following example, the enumeration Student is explicitly declared as public, so its members Name and Mark also have the access level public:

Example

public enum Student { case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("Student name:\(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("Student score:\(Mark1),\(Mark2),\(Mark3)") }

The output of the above program execution is:

学生成绩: 98,97,95

Subclass Access Control

The access level of a subclass must not be higher than that of its superclass. For example, if the superclass has an internal access level, the subclass cannot be declared as public.

Example

public class SuperClass { fileprivate func show() { print("Superclass") } } // The access level cannot be higher than the superclass: public > internal internal class SubClass: SuperClass { override internal func show() { print("Subclass") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

The output of the above program execution is:

Superclass

Access Control for Constants, Variables, Properties, and Subscripts

Constants, variables, and properties cannot have a higher access level than their types.

For example, if you define a property with a public access level but its type is private, the compiler will not allow it.

Similarly, a subscript cannot have a higher access level than its index type or return type.

If the defined type of a constant, variable, property, or subscript index is private, then they must explicitly declare their access level as private:

private var privateInstance = SomePrivateClass()

Getter and Setter Access Control

The access levels of Getters and Setters for constants, variables, properties, and subscript indexes are inherited from the access level of the member they belong to.

The access level of a Setter can be lower than the corresponding Getter's access level, allowing you to control the read and write permissions of variables, properties, or subscript indexes.

Example

class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("Counter:\(newTotal)") } didSet{ if counter > oldValue { print("New increment amount\(counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800

The access level of counter is fileprivate, so it can be accessed within the file.

The output of the above program execution is:

计数器: 100
新增加数量 100
计数器: 800
新增加数量 700

Initializer and Default Initializer Access Control

Initialization

We can declare access levels for custom initializers, but they must not be higher than the access level of the class they belong to. However, required initializers are an exception; their access level must be the same as the access level of the class they belong to.

Just like function or method parameters, the access levels of initializer parameters cannot be lower than the initializer's access level.

Default Initializer Methods

Swift provides a default no-parameter initializer for structures and classes to provide assignment operations for all their properties, but it does not give specific values.

The access level of a default initializer is the same as the access level of its containing type.

Example

Use the required keyword before the init() method of each subclass to declare access permissions.

Example

class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()

The output of the above program execution is:

10
30
10

Protocol Access Control

If you want to explicitly declare an access level for a protocol, note that you must ensure the protocol is only used within the access level scope you declared.

If you define a protocol with a public access level, the required functions that implement the protocol will also have a public access level. This is different from other types. For example, for other types with a public access level, their members have an internal access level.

Example

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // local storage init(no1: Int) { self.no1 = no1 // initialization } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Requires only one parameter for convenient method required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res is: \(res.no1)") print("res is: \(show.no1)") print("res is: \(show.no2)")

The output of the above program execution is:

res is: 20
res is: 30
res is: 50

Extension Access Control

You can extend classes, structures, and enumerations when conditions allow. Extension members should have the same access level as members of the original class. For example, if you extend a public type, your newly added members should have the same default internal access level as the original members.

Alternatively, you can explicitly declare the access level of the extension (such as using private extension) to declare a new default access level for all members in the extension. This new default access level can still be overridden by access levels declared by individual members.


Generic Access Control

The access level of a generic type or generic function is the lowest access level among the generic type, the function itself, and the generic type parameters.

Example

public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("Generic") print(tos.items) tos.push("Type Parameter") print(tos.items) tos.push("Type Parameter Name") print(tos.items) let deletetos = tos.pop()

The output of the above program execution is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

Type Aliases

Any type alias you define will be treated as a different type for access control purposes. The access level of a type alias cannot be higher than the access level of the original type.

For example, a private-level type alias can be set for a public, internal, or private type, but a public-level type alias can only be set for a public type, and cannot be set for internal or private types.

Note: This rule also applies when naming aliases for related types to satisfy protocol conformance.

Example

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("Generic") print(tos.items) tos.push("Where Statement") print(tos.items) var eos = ["Swift", "Generic", "Where Statement"] print(eos)

The output of the above program execution is:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]
Other Extensions