English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Access control can limit the access level of code in other source files or modules to your code.
You can explicitly set the access level for a single type (class, structure, enumeration), as well as for properties, functions, initialization methods, basic types, and index subscripts of these types.
Protocols can also be limited to a certain scope of use, including global constants, variables, and functions within 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, a module can use the import keyword to introduce another module.
A source file is a single source code file, which usually belongs to a module. A source file can contain multiple class and function definitions.
Swift provides four different access levels for entities in code: public, internal, fileprivate, and private.
Access Level | Definition |
---|---|
public | Can access any entity within the source files of its own module, and others can also access all entities within the source files by importing the module. |
internal | Can access any entity within the source files of its own module, but others cannot access the entities within the source files of the module. |
fileprivate | Private within the file, accessible only within the current source file. |
private | Accessible only within the class, and cannot be accessed outside the scope of the class or structure. |
Public is the highest access level, and private is the lowest access level.
Declare the access level of entities using modifiers such as public, internal, fileprivate, and private:
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 all use the default access level internal.
class SomeInternalClass {} // Access level is internal let someInternalConstant = 0 // Access level is internal
The access level of the function needs to be determined based on the access level of the function's parameter and return types.
The following example defines a global function named someFunction and does not explicitly declare its access level.
func someFunction() -> (SomeInternalClass, SomePrivateClass) { // Function Implementation {}
The access level of one of the classes in the function, SomeInternalClass, is internal, and the access level of the other SomePrivateClass is private. Therefore, according to the tuple access level principle, the access level of this tuple is private (the access level of the tuple is consistent with the type with the lowest access level in the tuple).
Because the access level of the function's return type is private, you must use the private modifier to explicitly declare the function:
private func someFunction() -> (SomeInternalClass, SomePrivateClass) { // Function Implementation {}
Declaring the function as public or internal, or using the default access level internal, is incorrect because in this way you will not be able to access the private level return values.
The access level of the members in an enum inherits from the enum itself, and you cannot declare different access levels for the members of an enum separately.
For example, in the following instance, the enum Student is explicitly declared as public level, so the access level of its members Name, Mark is also public:
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 grades: (Mark1),(Mark2),(Mark3) {}
The output result of the above program is:
Student grades: 98,97,95
The access level of a subclass cannot be higher than that of its superclass. For example, if the access level of the superclass is internal, the access level of the subclass cannot be declared as public.
public class SuperClass { fileprivate func show() { print("Superclass") {} {} // Access level cannot be higher than superclass internal > public internal class SubClass: SuperClass { override internal func show() { print("Subclass") {} {} let sup = SuperClass() sup.show() let sub = SubClass() sub.show()
The output result of the above program is:
Superclass Subclass
Constants, variables, and properties cannot have an access level higher than their type.
For example, if you define a public-level property but its type is private-level, this is not allowed by the compiler.
Similarly, the access level of an index cannot be higher than the index type or return type.
If the definition type of constants, variables, properties, and index accessors is at the private level, then they must explicitly declare the access level as private:
private var privateInstance = SomePrivateClass()
The access levels of getters and setters of constants, variables, properties, and index subscripts inherit from the access levels of the members they belong to.
The access level of setters can be lower than the access level of the corresponding getters, so that read and write permissions of variables, properties, or index subscripts can be controlled.
class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("Counter: \(newTotal)") {} didSet{ if counter > oldValue { print("Newly Added Quantity (counter - oldValue") {} {} {} {} let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800
The access level of counter is fileprivate, which can be accessed within the file.
The output result of the above program is:
Counter: 100 Newly Added Quantity 100 Counter: 800 Newly Added Quantity 700
We can declare the access level of a custom initialization method, but it must not be higher than the access level of the class it belongs to. However, the required initializer is an exception, its access level must be the same as the access level of the class it belongs to.
Like function or method parameters, the access level of initialization method parameters cannot be lower than the access level of the initialization method.
Swift provides a default no-argument initialization method for structures and classes, which provides assignment operations for all their properties but does not give specific values.
The access level of the default initialization method is the same as the access level of the type it belongs to.
Use the required keyword to declare access permissions before the init() method of each subclass.
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 result of the above program is:
10 30 10
If you want to explicitly declare the access level of a protocol, you need to pay attention to the fact that you must ensure that the protocol is only used within the scope of the declared access level.
If you define a public access level protocol, then the necessary functions provided by the protocol implementation will also be public access level. This is different from other types, such as, other types with public access level, whose members' access level is internal.
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.no"1) print("res is: \(show.no"1) print("res is: \(show.no"2)
The output result of the above program is:
res is: 20 res is: 30 res is: 50
You can extend classes, structures, and enumerations under conditions that allow it. The access level of the extension members should be consistent with the original class members. For example, if you extend a public type, the new members you add should have the same default internal access level as the original members.
Alternatively, you can explicitly declare the access level of an extension (such as using private extension) to give all members within that extension a new default access level. This new default access level can still be overridden by the access level explicitly declared for individual members.
The access level of a generic type or generic function takes the lowest access level among the generic type, function itself, and generic type parameters.
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("Generics") print(tos.items) tos.push("Type Parameter") print(tos.items) tos.push("Type Parameter Name") print(tos.items) let deletetos = tos.pop()
The output result of the above program is:
["Swift"] ["Swift", "Generics"] ["Swift", "Generics", "Type Parameter"] ["Swift", "Generics", "Type Parameter", "Type Parameter Name"]
Any type alias you define will be treated as a different type to facilitate access control. 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 assigned to a public, internal, or private type, but a public-level type alias can only be assigned to a public-level type and cannot be assigned to an internal or private-level type.
Note: This rule also applies to the situation where aliases are named for related types to meet protocol consistency.
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("Generics") print(tos.items) tos.push("Where Statement") print(tos.items) var eos = ["Swift", "Generics", "Where Statement"] print(eos)
The output result of the above program is:
["Swift"] ["Swift", "Generics"] ["Swift", "Generics", "Where Statement"] ["Swift", "Generics", "Where Statement"]