Why you need Access Control
Access control can restrict access to your code from other source files or modules. This feature allows you to hide the actual implementation of your code, making it more encapsulated.
Five keywords
For access control, Swift provides five keywords. The access priorities are open, public, internal, Fileprivate, and private in descending order.
Here’s a summary of the differences between these keywords:
- Open: This module is accessible to other modules and only applies to classes or class members. Allow other modules to inherit or overwrite.
- Public: this module and other modules can access, other modules are not allowed to inherit or overwrite.
- Internal: this module can be accessed. Do not write the access control keyword. Default is internal.
- Fileprivate: current source file access.
- Private: Only allowed in the current definition body.
One other thing to note about public is that when we use public to refer to a type, the type is public, but its members and methods are internal by default. If you want it to be called by other modules, you must explicitly use the public modifier. This is done to prevent the internal code of this type from being exposed as public.
guidelines
No entity can be defined in terms of another entity that has a lower (more restrictive) access level.
An entity may not be more than defined by entities with lower access levels.
Code examples:
fileprivate class Student { }
// Error: Constant cannot be declared public because its type 'Student' uses a fileprivate type
public let stu = Student()
Copy the code
As shown in the code above, stu is decorated with public and Student uses Fileprivate. This causes stu to have higher access rights than Student, resulting in compiler errors. Change Student to public or open to eliminate the compiler error.
Code examples:
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
Copy the code
SomePublicClass is public, but someInternalProperty is internal.
Tuples
Access control of the tuple type ≤ the smallest of the tuple types.
struct Dog { }
fileprivate struct Cat { }
fileprivate var ani: (Dog, Cat)
Copy the code
Because fileprivate is smaller than internal, ANI can only use the Fileprivate or private modifier, otherwise there will be compiler errors.
The generic
Access control for generic types must be less than or equal to the type access level and the minimum access level for all generic type parameters.
struct Dog { }
fileprivate struct Cat { }
public struct Person<T1, T2> { }
fileprivate var p = Person<Cat, Dog>()
Copy the code
In the above code, although Person uses the public modifier, Cat uses fileprivate, which is smaller than public and internal. So p access modifiers can only use fileprivate or private modifiers, otherwise there will be compiler errors.
Members and nested types
Access control for types affects access control for members (attributes, methods, constructors, subscripts), nested types.
- In general, if the type is Fileprivate or private, the member and nested types also default to Fileprivate or private.
- In general, if the type is internal or public, the member and nested types default to public.
public class SomePublicClass { // explicitly public class
public var somePublicProperty = 0 // explicitly public class member
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
class SomeInternalClass { // implicitly internal class
var someInternalProperty = 0 // implicitly internal class member
fileprivate func someFilePrivateMethod() {} // explicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
fileprivate class SomeFilePrivateClass { // explicitly file-private class
func someFilePrivateMethod() {} // implicitly file-private class member
private func somePrivateMethod() {} // explicitly private class member
}
private class SomePrivateClass { // explicitly private class
func somePrivateMethod() {} // implicitly private class member
}
Copy the code
Rewriting of members
- Access control for overridden members of a subclass must be ≥ the subclass or ≥ the overridden member of the parent class.
- A member of a parent class cannot be overridden by a child class defined outside the member scope.
public class Person {
private var age = 0
}
// Error: Property does not override any property from its superclass
class Student: Person {
override var age: Int {
set {}
get { 10 }
}
}
Copy the code
Because the age access control in the subclass is internal and the age access control in the parent class is private, internal ≥ private (the overriding member access control of the subclass is ≤ the overriding member of the parent class, which does not comply with the first clause of 👆), the compiler error is caused. Remove private to eliminate the error.
If you do not want to delete private, you can also modify the following:
public class Person {
private var age = 0
class Student: Person {
override var age: Int {
set {}
get { 10 }
}
}
}
Copy the code
Because age is scoped to the entire curly brace of Person, this is consistent with article 2 of 👆.
Getter and Setter
By default, the access control of get/set is the same as that of the owning environment. That is, if the type is private, get/set is also private.
In everyday development, we often run into the problem of allowing someone to read the value of this property, but not modify it. How do you do that? The answer is use private(set).
public class Person {
private(set) var age = 0
}
Copy the code
Age is externally readable but not writable.
The constructor
- If another module wants to call the default constructor of a class that is decorated with public, it needs to explicitly decorate the default constructor with public. Because the default constructor is internal.
- If the structure has a fileprivate, private storage property, the member constructor should also expect consistency.
Enum
- All cases are automatically consistent with enum access control.
- Case Access control cannot be set separately.
public enum CompassPoint {
case north // public
case south // public
case east // public
case west // public
}
Copy the code
All cases of CompassPoint are public, and you can’t set any other access control for them.
Protocol
The content defined in the protocol is automatically consistent with the access control of the type. The access control cannot be set separately.
See if the following code compiles:
public protocol PublicProtocol {
func test() // public
}
public class PublishClass: PublicProtocol {
func test() { }
}
Copy the code
The PublishClass is internal, and the public protocol’s test() is public. The correct code is as follows:
public class PublishClass: PublicProtocol {
public func test() { }
}
Copy the code
Extension
- If extension’s access control is explicitly set, extension members automatically receive extension’s access control.
struct Student { }
private extension Student {
func run() { } // private
}
Copy the code
- If not explicitly set, members in extension have the same access control as those defined in the type.
extension Student {
func write() { } // internal
}
Copy the code
- You can set access control for members in the Extension individually.
extension Student {
private func eat() { } // private
func read() { } // internal
}
Copy the code
- Extension access control cannot be explicitly set for an extension that is used to comply with the protocol.
protocol TestProtocol {
func test()
}
// Error: 'internal' modifier cannot be used with extensions that declare protocol conformances
internal extension Student: TestProtocol {
func test() { }
}
Copy the code
- Apple Document