Access control can limit the level of access to your code by code in other source files or modules.
You can explicitly set access levels for individual types (classes, structs, enumerations), or for attributes, functions, initialization methods, basic types, subscript indexes, etc. of these types.
The protocol can also be used within a certain range, including the global constants, variables and functions in the protocol.
Access control is based on modules and source files.
Modules refer to frameworks or applications built and published in separate units. One module in Swift can use the import keyword to introduce another module.
Source file is a single source file. It usually belongs to a module. 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 | You can access any entity in the source file in your module, and others can access all entities in the source file by introducing the module. |
internal | You can access any entity in the source file of your module, but no one else can access the entity in the source file of that module. |
fileprivate | The file is private and can only be used in the current source file. |
private | It can only be accessed in a class, but it cannot be accessed outside the scope of the class or structure. |
public is the highest level of access and private is the lowest level of access.
Grammar
Declare the access level of entities by modifiers public, internal, fileprivate, 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() {}
2. Function Type Access Permissions
The access level of a function needs to be derived from the access level of the parameter type and the return type of the function.
The following example defines a global function called someFunction and does not explicitly state its access level.
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// Function realization
}
One of the class SomeInternalClass in the function has internal access level, and the other SomePrivateClass has private access level. So according to the principle of tuple access level, the access level of the tuple is private (the access level of the tuple is the same as that of the lowest type in the tuple).
Because the access level of the function's return type is private, you must explicitly declare the function using the private modifier:
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
// Function realization
}
It would be wrong to declare this function public or internal, or to use the default access level internal, because you would not be able to access the return value of the private level.
3. Enumeration of Type Access Permissions
The access level of a member in an enumeration inherits from that enumeration, and you cannot declare different access levels for the member in the enumeration separately.
Example
For example, in the following example, enumeration Student is explicitly declared as a public level, and its members Name and Mark have access levels of public as well:
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 achievement: \(Mark1),\(Mark2),\(Mark3)")
}
The output of the above program is as follows:
Student Achievements: 98, 97, 95
IV. Subclass Access Permissions
Access levels of subclasses must not be higher than those of parent classes. For example, if the access level of the parent class is internal, the access level of the child class cannot be declared public.
Example
public class SuperClass {
fileprivate func show() {
print("Superclass")
}
}
// Access level should not be lower 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 of the above program is as follows:
Superclass Subclass
6. Constants, variables, attributes, subscript access rights
Constants, variables, and attributes cannot have higher access levels than their types.
For example, you define a public level attribute, but its type is private level, which is not allowed by the compiler.
Similarly, subscribers cannot have higher access levels than index or return types.
If the definitions of constants, variables, attributes, and subscript indexes are private, they must explicitly state that the access level is private:
private var privateInstance = SomePrivateClass()
Getter and Setter access rights
Access levels for constants, variables, attributes, subscripted indexes of Getters and Setters inherit from the access levels of their members.
Setter access level can be lower than the corresponding Getter access level, so you can control the read and write permissions of variables, attributes or subscript indexes.
Example
class Samplepgm {
fileprivate var counter: Int = 0{
willSet(newTotal){
print("Counter: \(newTotal)")
}
didSet{
if counter > oldValue {
print("New additions \(counter - oldValue)")
}
}
}
}
let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800
counter has fileprivate access level and can be accessed within a file.
The output of the above program is as follows:
Counter: 100 New increase of 100 Counter: 800 700 new additions
8. Access permissions for constructors and default constructors
1. Initialization
We can declare the access level for a custom initialization method, but not higher than the access level of the class it belongs to. Except for the necessary constructor, its access level must be the same as that of the class it belongs to.
Like function or method parameters, the access level of initialization method parameters can not be lower than that of initialization method.
2. Default initialization method
Swift provides a default parametric initialization method for structured objects and classes to provide assignment operations for all their attributes, but no specific values are given.
The access level of the default initialization method is the same as that of the type to which it belongs.
3. Example
Use the required keyword to declare access rights 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 of the above program is as follows:
10 30 10
Protocol Access Rights
If you want to explicitly declare the access level for a protocol, one thing to note is that you need to ensure that the protocol is used only in the scope of the access level you declare.
If you define a public access level protocol, the necessary function to implement it will also be the public access level. This is different from other types, such as other types of public access level whose members have 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 is as follows:
res is: 20
res is: 30
res is: 50
X. Extending Access Permissions
You can extend classes, structures, and enumerations as conditions permit. Extended members should have the same level of access as members of the original class. For example, if you extend a common type, your new members should have the same default internal access level as the original members.
Alternatively, you can explicitly state the access level of the extension (for example, using private extension) and declare a new default access level to all members of the extension. This new default access level can still be overridden by the access level declared by individual members.
11. Generic Access Permissions
The access level of generic type or function is the lowest among generic type, function itself and parameter of generic type.
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 paradigm")
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 is as follows:
["Swift"]
["Swift", "generic paradigm"]
["Swift", "generic paradigm", "Type parameter"]
["Swift", "generic paradigm", "Type parameter", "Type parameter name"]
Twelve. Type aliases
Any type alias you define will be treated as a different type for easy access control. The access level of a type alias cannot be higher than that of the original type.
For example, a private level type alias can be set to a public, internal, private type, but a public level type alias can only be set to a public level type, not to an internal or private level type.
Note: This rule also applies when aliases are given to 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("generic paradigm")
print(tos.items)
tos.push("Where Sentence")
print(tos.items)
var eos = ["Swift", "generic paradigm", "Where Sentence"]
print(eos)
The output of the above program is as follows:
["Swift"]
["Swift", "generic paradigm"]
["Swift", "generic paradigm", "Where Sentence"]
["Swift", "generic paradigm", "Where Sentence"]