In Swift 4, since now private is visible in extensions also in the same source code file, how is it different from the fileprivate access modifier?
Background: In Swift 3, private variables in a class are not visible in its extensions in the same file. For that, fileprivate had to be used.
File Private
File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
Syntax: fileprivate <var type> <variable name>
Example: fileprivate class SomeFilePrivateClass {}
Private
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Syntax: private <var type> <variable name>
Example: private class SomePrivateClass {}
Here is more detail about all access levels: Swift - Access Levels
Answer to your question:
(In Swift 3, private variables in a class are not visible in its extensions in the same file. For that, fileprivate had to be used.)
Yes, in Swift 4.0, Private is now accessible in extension but within same file. If you declare/define extension in other file, then your private variable will not be accessible to your extension
Look at this images:
File: ViewController.swift
Here extension and view controller both are in same file, hence private variable testPrivateAccessLevel is accessible in extension
File: TestFile.swift
Here extension and view controller both are in different files, hence private variable testPrivateAccessLevel is not accessible in extension.
Here class ViewController2 is a subclass of ViewController and both are in same file. Here private variable testPrivateAccessLevel is not accessible in Subclass but fileprivate is accessible in subclass.
Applicable in swift 4.0 and its versions
Private
Private access only in class and its extension(When extension is in the same .swift file).
File Private
File-private access only in class and its extension & subClass(When extension or subClass is in the same .swift file).
///////////////ViewController1.swift file
class ViewController1 {
private func testPrivate() {
print("testPrivate")
}
fileprivate func testFilePrivate() {
print("testFilePrivate")
}
func doesNothing1() {
testPrivate() //success
testFilePrivate() //success
}
}
extension ViewController1 {
func doesNothingInExtensionSameFile() {
testPrivate() //success
testFilePrivate() //success
}
}
class SomeOtherClassInSameFile {
let vc1 = ViewController1()
func doesNothing() {
vc1.testPrivate() //throws error
vc1.testFilePrivate() //success
}
}
////////////// ViewController2.swift file
extension ViewController1 {
func doesNothingInExtensionDifferentFile() {
testPrivate() //throws error
testFilePrivate() //throws error
}
}
private and fileprivate access levels have come closer with Swift4.
The difference in access lies as follows:
fileprivate members - only & entirely within that .swift file
private members - only in that class & extension of the class if both are present in same .swift file
Hence only fileprivate members(not private) can be accessed in
Sub Classes in the same .swift file
Instances of the class (initialized in another class) in the same .swift file.
Open Vs Public:
Public does not allow a class to be inherited in another
module/target whereas Open does.
Public method does not allow to be overridden in subclass in another
module/target whereas Open does.
Apart from above both are same.
Private Vs Fileprivate:
(Within single file) Private does not allow to access (func and properties) in subclass whereas FilePrivate does.
(Outside File) Private and FilePrivate both can't be accessible.
Apart from above both are same.
"Private" is accessible only in class, "FilePrivate" accessible only in .swift file.
Private : Access in Class and Class Extension.
FilePrivate : Access in class, subClass, Extension,
Related
Is there a way to make the extension able to access private var defined in the definition file, but keep the extension's private function private to that extension only (not assessable in definition file)?
For example:
class MyClass {
private var myStr = "str"
func doSomething() {
funcA() // <- should cause compiler error
}
}
extension MyClass {
private func funcA() {
print(myStr)
}
}
So doSomething() can not call funcA(), but funcA() can access myStr.
Yes. Put the definition in one file, and the extension in another.
Private access restricts the use of an entity to the enclosing
declaration, and to extensions of that declaration that are in the
same file.
That information is not quite complete. As you've seen, anything private in an extension is accessible in the declaration, and other extensions. But only in the same file.
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html
Person.swift
class Person {
var name: String
init(name: String){
self.name = name
}
func greet() {
print("Hello, I am \(name)")
}
}
Workplace.swift
class WorkPlace {
var employees: [Person]
init(employees: [Person]) {
self.employees = employees
}
func greet() {
for employee in employees {
employee.customGreeting()
}
}
}
private extension Person {
func customGreeting() {
print("Hi")
}
}
Here I have a simple class called Person defined in Person.swift and a Workplace class defined in Workplace.swift.
The following is an excerpt taken from the Swift language guide (Access Control)
Alternatively, you can mark an extension with an explicit access-level modifier (for example, private) to set a new default access level for all members defined within the extension.
According to this I would expect the method customGreeting in the private extension of Place not be visible inside Person.swift as it would be a private method and it is defined in a different file than the one the class is declared in, which is exactly what happens. I would also expect that method to not be visible inside Workplace.swift but it does. I am able to compile this code without any errors.
If I mark the method as private explicitly then it is not visible inside Workplace.swift. Shouldn't specifying the extension as private be enough to make the method private like it says in the language guide?
private extension Person {
private func customGreeting() {
print("Hi")
}
}
I know that this is a contrived example and I am just trying to get a clear picture of how access control works in swift.
The issue isn't that methods declared in a private extension available in other classes, but rather, more narrowly, that private qualifier to an extension produces fileprivate behavior.
This is explicitly acknowledged in SE-0025, which says:
As before, an extension with an explicit access modifier overrides the default internal access by specifying a default scope. Therefore, within an extension marked private, the default access level is fileprivate (since extensions are always declared at file scope). This matches the behavior of types declared private at file scope.
This seems inconsistent with their broader statement:
... you can mark an extension with an explicit access-level modifier (for example, private) 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.
While there is an inconsistency here, it would appear to be a conscious decision.
This article has been helpful in understanding the new access specifiers in Swift 3. It also gives some examples of different usages of fileprivate and private.
My question is - isn't using fileprivate on a function that is going to be used only in this file the same as using private?
fileprivate is now what private used to be in earlier
Swift releases: accessible from
the same source file. A declaration marked as private can now only be accessed within the lexical scope it is declared in.
So private is more restrictive than fileprivate.
As of Swift 4, private declarations inside a type are accessible to extensions of the same type if the extension is defined in the same source file.
Example (all in one source file):
class A {
private func foo() {}
fileprivate func bar() {}
func baz() {
foo()
bar()
}
}
extension A {
func test() {
foo() // Swift 3: error: use of unresolved identifier 'foo'
// Swift 4: no error because extension is in same source file
bar()
}
}
let a = A()
a.foo() // error: 'foo' is inaccessible due to 'private' protection level
a.bar()
The private foo method is accessible only within the scope of
the class A { ... } definition. It is not even accessible from
an extension to the type (in Swift 3, see the second note below for
changes in Swift 4).
The file-private bar method is accessible from the same source file.
Notes:
The proposal SE-0159 – Fix Private Access Levels suggested to revert to the Swift 2 semantics in Swift 4. After a lengthy and controversial discussion on the swift-evolution mailing list, the proposal was rejected.
The proposal SE-0169 – Improve Interaction Between private Declarations and Extensions suggests to make private
declarations inside a type accessible to extensions of the same type
if the extension is defined in the same source file.
This proposal was accepted and implemented in Swift 4.
I just draw a diagram about private, fileprivate, open and public
Hope it can quickly help you , for text description please refer to Martin R 's answer
[ Update Swift 4, 5 ]
Updated for Swift 5
Private vs FilePrivate
For better clarity paste the code snippet in Playground
class Sum1 {
let a: Int!
let b: Int!
private var result: Int?
fileprivate var resultt: Int?
init(a : Int, b: Int) {
self.a = a
self.b = b
}
func sum(){
result = a + b
print(result as! Int)
}
}
let aObj = Sum1.init(a: 10, b: 20)
aObj.sum()
aObj.resultt //File Private Accessible as inside same swift file
aObj.result //Private varaible will not be accessible outside its definition except extensions
extension Sum1{
func testing() {
// Both private and fileprivate accessible in extensions
print(result)
print(resultt)
}
}
//If SUM2 class is created in same file as Sum1 ---
class Sum2{
func test(){
let aSum1 = Sum1.init(a: 2, b: 2)
// Only file private accessible
aSum1.resultt
}
}
Note: Outside of Swift file both private and fileprivate are not accessible.
A practical rule of thumb is that you use private for variables, constants, inner structs and classes that are used only inside the declaration of your class / struct. You use fileprivate for things that are used inside of your extensions within the same file as your class/struct but outside of their defining curly braces (ie. their lexical scope).
class ViewController: UIViewController {
#IBOutlet var tableView: UITableView!
//This is not used outside of class Viewcontroller
private var titleText = "Demo"
//This gets used in the extension
fileprivate var list = [String]()
override func viewDidLoad() {
navigationItem.title = titleText
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return list.count
}
}
In Swift 4.0, Private is now accessible in extension but within same file. If you declare/define extension in other file, then your private variable will not be accessible to your extension**
File Private
File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
Syntax: fileprivate <var type> <variable name>
Example: fileprivate class SomeFilePrivateClass {}
Private
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
Syntax: private <var type> <variable name>
Example: private class SomePrivateClass {}
Here is more detail about all access levels: Swift - Access Levels
Look at this images:
File: ViewController.swift
Here extension and view controller both are in same file, hence private variable testPrivateAccessLevel is accessible in extension
File: TestFile.swift
Here extension and view controller both are in different files, hence private variable testPrivateAccessLevel is not accessible in extension.
Here class ViewController2 is a subclass of ViewController and both are in same file. Here private variable testPrivateAccessLevel is not accessible in Subclass but fileprivate is accessible in subclass.
Although #MartinR's and #StephenChen's answer are perfect, Swift 4 changes things a little bit.
Private is now considered as private to a class in which it is declared and also to its extensions.
FilePrivate is considered to be private in that file be it a class in which the variable is defined, it's extension, or any other classes defined in that same file.
filePrivate - access controll level is within the file.
case 1: If we create extension with in same class file and try to access fileprivate function or fileprivate property in its extension - access allowed
case 2: If we create a extension of class in new file - And now try to access fileprivate function or fileprivate property - access not allowed
private - access control level is with in lexical scope
case 1: If property or function is declared as private in class - then scope is by default the class.
case 2: if private instance is declared with in function body - then scope of instance is limited to function body.
This is the explanation for swift 4. For swift 3, the difference is the private. swift 3 private cannot be accessed by its extension, only Class A itself can access.
After swift 4, fileprivate becomes a bit redundant, because person normally will not define the subclass in the same file. Private should be enough for most cases.
In the following example, language constructs modified by private and fileprivate seem to behave identically:
fileprivate func fact(_ n: Int) -> Int {
if (n == 0) {
return 1
} else {
return n * fact(n - 1)
}
}
private func gauss(_ n: Int) -> Int {
if (n == 0) {
return 0
} else {
return n + gauss(n - 1)
}
}
print(fact(0))
print(fact(5))
print(fact(3))
print(gauss(10))
print(gauss(9))
This is according to intuition, I guess. But, is there any exception?
Kindest regards.
class Privacy {
fileprivate(set) var pu:Int {
get {
return self.pr
}
set {
self.pr = newValue
}
}
private var pr:Int = 0
fileprivate var fp:Int = 0
func ex() {
print("\(self.pu) == \(self.pr) and not \(self.fp)")
}
}
extension Privacy {
func ex2() {
self.pu = 5
self.ex()
}
}
I like this because it is super simple for ivars.
Try changing fileprivate to private (and vice versa) and see what happens on compile...
In Objective-C category, you can bring in the extended capability introduced by the category methods by including the header of the category in your class.
It seems like all Swift extensions are automatically introduced without import. How do you achieve the same thing in Swift?
For example:
extension UIView {
// only want certain UIView to have this, not all
// similar to Objective-C, where imported category header
// will grant the capability to the class
func extraCapability() {
}
}
Define a protocol that will serve as a selection, wether the extensions should be available or not:
protocol UIViewExtensions { }
then define an extension for the protocol, but only for subclasses of UIView (the other way around won't work):
extension UIViewExtensions where Self: UIView {
func testFunc() -> String { return String(tag) }
}
A class that is defined to have the protocol will also have the extension:
class A: UIView, UIViewExtensions { }
A().testFunc() //has the extension
And if it is not defined to have the protocol, it will also not have the extension:
class B: UIView {}
B().testFunc() //execution failed: MyPlayground.playground:17:1: error: value of type 'B' has no member 'testFunc'
UPDATE
Since protocol extensions don't do class polymorphism, if you need to override functions, the only thing I can think of is to subclass:
class UIViewWithExtensions: UIView {
override func canBecomeFocused() -> Bool { return true }
}
UIViewWithExtensions().canBecomeFocused() // returns true
this could also be combined with the extension, but I don't think it would still make much sense anymore.
You can make extensions private for a particular class by adding private before the extension like so
private extension UIView {
func extraCapability() {
}
}
This will mean it can only be used in that particular class. But you will need to add this to each class that requires this extension. As far as I know there is no way to import the extension like you can in Obj-c
NOTE
Private access in Swift differs from private access in most other languages, as it’s scoped to the enclosing source file rather than to the enclosing declaration. This means that a type can access any private entities that are defined in the same source file as itself, but an extension cannot access that type’s private members if it’s defined in a separate source file.
According to Apple, here, it does not appear you can make extensions private in separate files.
You can create a private extension in the same source file.
in this example getInstance is public so it makes sense to be accessible but the private variable was accessible as well, why?
public class SingleObject {
public struct Static {
private static var object: SingleObject?
public static func getObject() -> SingleObject {
if (object == nil) {
object = SingleObject()
}
return object!
}
}
}
SingleObject.Static.object //why private is accessible here?
SingleObject.Static.getObject()
As of Swift 3, the example code no longer compiles. A new modifier called fileprivate was added and works the way private used to work (i.e. it can be seen by anything else in the same file).
private in Swift 3 and 4 now works more like you would expect it to - in Swift 3, it can only be accessed within the context it was declared in, while in Swift 4 it can also be accessed from extensions to that type (but only if they're in the same file).
The private access modifier makes a declaration private to the file and not to the class or struct. That sounds a little weird, I know, but it's how it's implemented in swift.
The other 2 access modifiers are:
internal: accessible in the current module only
public: accessible from anywhere
Suggested reading: Access Control
The private keyword means that the variable is only accessible within the same file not within the same class.
You are accessing the property from within the same file, which is why it is working. Try doing it from a different file and you will see that it is not accessible.