7

I've just found out that Swift's private access modifier is file level, as stipulated in the docs under "Access Levels":

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.

So this means the following code will compile when the types are in the same file:

class FirstType {
    private var privateProperty: String = ""
}

class SecondType {
    private let firstType = FirstType()

    func messWithFirstType() {
        firstType.privateProperty = "" // this compiles and works!
    }
}

As far as I can see, this breaks encapsulation completely. On the other hand, it might be nice to have some related types grouped together in the same file for readability, especially if the related types are tiny, like enums.

Private extensions are an exception because they are extending the same type the file is meant to contain. And private extensions do bring some nice things.

Are there any other reason, apart from facilitating private extensions, for the file scope private access modifier to be in Swift?

Allen Zeng
  • 2,635
  • 2
  • 20
  • 31
  • 2
    I wouldn't be so fast to say it breaks encapsulation. If you have two classes that aren't related to each other (and shouldn't know about each others' internals) then they probably shouldn't be in the same source file. – Craig Otis Jun 10 '16 at 12:03
  • 4
    Related: [Access Control and protected](https://developer.apple.com/swift/blog/?id=11) in the Swift blog. – Martin R Jun 10 '16 at 12:04
  • @CraigOtis what types could be defined as "related" so they can mess with each other's internals though? Two discrete classes shouldn't know about each other's private components period. I'm leaning towards having all types in their own source files, even tiny ones. – Allen Zeng Jun 10 '16 at 13:28
  • 1
    I've seen it commonly used to combine a class and an enum. For example, you have a `Recipe` class that maintains a `RecipeType` enum. I could see the argument for putting those two in the same file. You could maybe do the same thing with a `ListenerManager` and `BasicListener`, but honestly you'd probably want to separate them. I'm not saying you _should_ put two classes in the same file - rather, that you probably _shouldn't_ unless you have a really good reason. – Craig Otis Jun 10 '16 at 13:33
  • @MartinR thanks for the link, I've read it before but having gone through it again, it seems to imply that extensions are the only reason behind the behaviour, which is what I was thinking. – Allen Zeng Jun 10 '16 at 13:41
  • Justification required for keeping types together in the same file seems like a good approach. Although as types inevitably expand, it will be much easier to get the compiler to ensure private components are _actually_ off limits. So to me, keeping all types separate is more robust in the long term. – Allen Zeng Jun 10 '16 at 13:55

2 Answers2

7

It isn't clear to me why private was originally implemented with regard to files, but rest assured that the Swift folks know this is not the only possible meaning of private and that it isn't ideal for some purposes, and are working to change it. There's already a proposal on the table, accepted for Swift 3, that will turn the current private into fileprivate and add a new level of private that will be scoped to the type rather than the file. You can expect to see this become part of Swift 3 in the very near future.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Basically, you're just late to the party. You might enjoy reading the [discussion](http://thread.gmane.org/gmane.comp.lang.swift.evolution/9334) as it unfolded. – matt Jun 13 '16 at 01:50
  • 2
    Done in Swift 3.0 : https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID3 – KarimIhab Sep 27 '16 at 11:23
1

When you wonder about anything that seems "weird" in Swift, most of the time the answer is "because of Objective-C".

For some perspective, I consider 3 access level common to many modern programming languages:

  1. private: only accessible to the class in which it is defined.
  2. protected: only accessible to the class in which it is defined and its subclasses.
  3. public: accessible to any outside program.

Let's take one step further back, to the world of C. C has no access modifier whatsoever, not being an OOP language. However, in reality, it's closer to having a private / public system. If you want other programs to know your symbols (functions, macros, data types, etc.), you define them in your header (.h) file. If not, you define them in your source (.c) file or a private header file. Whatever program interested in your symbol will include the corresponding header file:

#include "foo.h"

This #include is no more than a compiler-assisted copy & paste. The compiler copies all the symbols in foo.h and redeclare them in your source file.

Since Objective-C is a strict superset of C, every valid C program is also a valid Objective-C program. Objective-C continues this tradition: declare your public methods in the header file, keep private methods declaration to the implementation file:

// ------------------------------------------
// MyClass.h
// ------------------------------------------
@interface MyClass: NSObject
- (void) publicMethod;
@end


// ------------------------------------------
// MyClass.m
// ------------------------------------------
#import "MyClass.h"

// Declare your private methods here.
// You can move this to a separate file, i.e. MyClass+Private.h if you want
@interface MyClass()
- (void) privateMethod;
@end

@implementation MyClas    
- (void) publicMethod () { ... }
- (void) privateMethod() { ... }
@end

So at glance, Objective-C seems to inherit C's system of public / private declarations. However, Objective-C is a very dynamic language. You can query its runtime for all methods to a class, private or public. Access control in Objective-C is more about "use what I tell you in the documentation" rather than "this method is off limit to you".

This poses a paradox: how do you implement protected in Objective-C? It has no good answer for that. One common pattern is to move all protected methods into a separate declaration file and import them into the main class and subclass's implementation:

// ------------------------------------------
// MyClass+Protected.h
// ------------------------------------------
@interface MyClass (Protected)
- (void) protectedMethod;
@end

// ------------------------------------------
// MySubClass.m
// ------------------------------------------
#import "MyClass+Protected.h"
...

Swift simply carries on that tradition, for better or worse. There is an accepted proposal to change this in Swift 3. If anything, Chris Lattner and the Swift team has shown little affinity for the past and the legacies of C. You can see evidence of that in Swift 2.2 with the removal of ++ and C-style for loops.

Code Different
  • 90,614
  • 16
  • 144
  • 163
  • Appreciate your answer. I do have my suspicions that this behaviour is somehow rooted in the Objective-C world. But it's still not quite clear to me why exactly it was done in the first place. – Allen Zeng Jun 13 '16 at 03:08
  • Some historical context: Apple hired Chris Lattner due to his impressive works on the Clang compiler. His first project at Apple was probably add ObjC support in Clang. Along the way, either he or some senior Apple employee proposed the idea of a brand new language that we now know as Swift. He had had deep involvement in ObjC. And once you get your mind trained in a certain way, you tend to follow your experience. – Code Different Jun 13 '16 at 03:47
  • Of course it could be more complicated than that, like some senior engineers at Apple get so used to the ObjC and pressured the Swift team to use the ObjC-style access modifier. I'm simple happy that it's about to change – Code Different Jun 13 '16 at 03:48
  • Makes sense. I did get some answers from the [discussions](http://thread.gmane.org/gmane.comp.lang.swift.evolution/9334) matt sent through, in particular [this response](http://article.gmane.org/gmane.comp.lang.swift.evolution/10008). – Allen Zeng Jun 13 '16 at 03:59