I think I probably have some blind spot. The following code in test target actually works that I thought it should not: (MyHelper is private already, but the caller still can use myHelperFunc())
// both MyClass and MyHelper are in the same file
class MyClass: XCTestCase {
func testDoWork() {
MyHelper.myHelperFunc()
}
}
private class MyHelper {
static func myHelperFunc() -> String {
return "something"
}
}
If I move the code to main target (delete the XCTestCase), compiler immediately flag MyHelper is not accessible that seems the right behavior? Is there something specific for test target that I missed?
private at file scope is equivalent to fileprivate.
#testable import makes internal code accessible to a test target.
Access Levels in The Swift Programming Language explains how private works:
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file.
"The enclosing declaration" of MyHelper is "the file." This is perhaps a bit more clearly stated in the Declaration Modifiers section of the Swift Language Reference:
private
Apply this modifier to a declaration to indicate the declaration can be accessed only by code within the declaration’s immediate enclosing scope.
Again, the "enclosing scope" of MyHelper is the file. If MyHelper were enclosed in some other class, then it would be limited to that scope rather than the whole file:
// Things change if MyHelper has a different enclosing scope than MyClass.
class MyClass {
func testDoWork() {
MyHelper.myHelperFunc() // Cannot find 'MyHelper' in scope
}
}
class C {
// MyClass can't access C.MyHelper now.
private class MyHelper {
static func myHelperFunc() -> String {
return "something"
}
}
}
In your example, myHelperFunc() has no annotation, so it initially receives the default level of internal. This is noted in Access Levels again:
Default Access Levels
All entities in your code (with a few specific exceptions, as described later in this chapter) have a default access level of internal if you don’t specify an explicit access level yourself.
However, as noted in "Guiding Principle of Access Levels:"
No entity can be defined in terms of another entity that has a lower (more restrictive) access level.
It is not allowed for MyHelper.myHelperFunc() to have a broader access level than its encoding type (MyHelper), so it's limited to file scope (which is effectively the same as fileprivate).
Related
I read the document about Swift 5.1 from swift.org and have some questions about access level in enumeration.
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID14
In the document, it says:
The individual cases of an enumeration automatically receive the same access level as the enumeration they belong to.
private enum SomePrivateEnum {
case one
case two
case three
}
private class SomePrivateClass {
private var somePrivateProperty = 0
}
// work
print(SomePrivateEnum.one)
// error: 'somePrivateProperty' is inaccessible due to 'private' protection level
print(SomePrivateClass().somePrivateProperty)
According to the document, if I have a private enum, then all cases should receive private access level. The question is, why can I access the private case outside the enum declaration ? This behavior is different from Class.
First of all, your code is completely artificial, as it would not even compile except in a playground — and in a playground the concepts of privacy are more or less meaningless. Test only within a real project.
When you do, you will have something like this:
private enum SomePrivateEnum {
case one
case two
case three
}
private class SomePrivateClass {
private var somePrivateProperty = 0
}
class ViewController : UIViewController {
func test() {
print(SomePrivateEnum.one)
print(SomePrivateClass().somePrivateProperty)
}
}
Now that we've established that, we can proceed to what's wrong with your test itself, namely that you are comparing apples with oranges. Here's the parallelism:
print(SomePrivateEnum.one) // ok
print(SomePrivateClass()) // ok
So private for SomePrivateEnum and private for SomePrivateClass mean the same thing, namely: "private within this file". This code is in the same file so it can see both SomePrivateEnum and SomePrivateClass. (As the docs tell you, code that can see SomePrivateEnum can see SomePrivateEnum.one, and vice versa. So we are now comparing apples with apples.)
But private for somePrivateProperty means something else. It means "private within this type". So only code inside SomePrivateClass can see somePrivateProperty. This code is not inside SomePrivateClass, so it can't see that property.
You can access private declarations inside current context.
for example, if you wrap enum with other context - it will not be accessible same way as property inside SomePrivateClass.
i.e. this will be inaccessible:
struct Foo {
private enum SomePrivateEnum {
case one
case two
case three
}
}
print(Foo.SomePrivateEnum.one)
and this will be:
private class SomePrivateClass {
var somePrivateProperty = 0
}
print(SomePrivateClass().somePrivateProperty)
I would like to hide some property setters and initializers on my Swift model objects. These are reference data that the server provides, and under no circumstances should they be created or modified by the application. This is simple enough in Swift.
However, there is application in my project (a separate target) that needs to break this rule. It is a tool I use to populate the data in bulk, so of course needs to be able to initialize new model objects and set their properties.
What are my options for accomplishing this? I would rather not use a completely new project since it will mean a lot of code duplication. Is there some language-level way to keep this mutability hidden from one application but available to another?
If you declare a property with the let keyword. It can then only be set in the init of the type.
You can also declare a private setter to make the property readonly from the caller of the type but read/write inside the type
struct Foo {
private(set) var bar: Bool = true
func toggle() {
bar.toggle()
}
}
var foo = Foo()
let barState = foo.bar // This works
foo.toggle() // This works too
foo.bar.toggle() // This will make a compile time error
In Swift, what is the conventional way to define the common pattern where a property is to be externally readonly, but modifiable internally by the class (and subclasses) that own it.
In Objective-C, there are the following options:
Declare the property as readonly in the interface and use a class extension to access the property internally. This is message-based access, hence it works nicely with KVO, atomicity, etc.
Declare the property as readonly in the interface, but access the backing ivar internally. As the default access for an ivar is protected, this works nicely in a class hierarchy, where subclasses will also be able to modify the value, but the field is otherwise readonly.
In Java the convention is:
Declare a protected field, and implement a public, read-only getter (method).
What is the idiom for Swift?
Given a class property, you can specify a different access level by prefixing the property declaration with the access modifier followed by get or set between parenthesis. For example, a class property with a public getter and a private setter will be declared as:
private(set) public var readonlyProperty: Int
Suggested reading: Getters and Setters
Martin's considerations about accessibility level are still valid - i.e. there's no protected modifier, internal restricts access to the module only, private to the current file only, and public with no restrictions.
Swift 3 notes
2 new access modifiers, fileprivate and open have been added to the language, while private and public have been slightly modified:
open applies to class and class members only: it's used to allow a class to be subclassed or a member to be overridden outside of the module where they are defined. public instead makes the class or the member publicly accessible, but not inheritable or overridable
private now makes a member visible and accessible from the enclosing declaration only, whereas fileprivate to the entire file where it is contained
More details here.
As per #Antonio, we can use a single property to access as the readOnly property value publicly and readWrite privately. Below is my illustration:
class MyClass {
private(set) public var publicReadOnly: Int = 10
//as below, we can modify the value within same class which is private access
func increment() {
publicReadOnly += 1
}
func decrement() {
publicReadOnly -= 1
}
}
let object = MyClass()
print("Initial valule: \(object.publicReadOnly)")
//For below line we get the compile error saying : "Left side of mutating operator isn't mutable: 'publicReadOnly' setter is inaccessible"
//object.publicReadOnly += 1
object.increment()
print("After increment method call: \(object.publicReadOnly)")
object.decrement()
print("After decrement method call: \(object.publicReadOnly)")
And here is the output:
Initial valule: 10
After increment method call: 11
After decrement method call: 10
I am still bit confused about the scope, I think the variables can only be accessed within its scope, that's what I've understand in a general way
class Car {
let manufacturer: String
private(set) var color: String
init() {
manufacturer = "Ford"
color = "Black"
}
func changeColor(color: String){
self.color = color
}
}
var carOfTim = Car()
carOfTim.changeColor("Red") // only "changeColor" fun can update the color
print(carOfTim.color)
//why I can do this?
carOfTim.color = "Green"
print(carOfTim.color) // it prints the "Green"!
Question: I think the private variable color can only be accessed by thechangeColor function, because changeColor has the same scope with color. But carOfTim.color = "Green" can still update the color variable, why?
Guess: Since I used the Xcode playground, everything is inputted into the same plain "terminal", therefore all of them might have the same scope, if I put the Car class into a different folder, carOfTim.color = "Green" may not working anymore.
Please correct me if I'm wrong. Thanks a lot for your time and help.
In Swift, private is scoped to the source file, not to the declaring entity. That's a design decision.
From the documentation:
Private access restricts the use of an entity to its own defining source file. Use private access to hide the implementation details of a specific piece of functionality.
...
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.
And in the examples below (bold is mine):
However, the access level for the numberOfEdits property is marked with a private(set) modifier to indicate that the property should be settable only from within the same source file as the TrackedString structure’s definition.
I have this Haxe class that is growing quite large. It consists mostly of static methods & properties. (It's a module set to compile as JS target).
I would like to separate some of the complex static functions into another class.
Is there any way to mark it with a metatag / indicate the other class is an "extension" to the original class?
Something like #:native("OriginalClass") class OtherClass {...}
The goal is to avoid having to write the full variable access (ex: OriginalClass.LOG_QUEUE vs. LOG_QUEUE) or clutter the imports with each OriginalClass's static methods / properties used at the top of the OtherClass. Basically, something to make it aware that it "is" using the same members as the OriginalClass (whenever an 'undefined' one is found, at compile-time).
Example:
If OriginalClass has static var LOG_QUEUE:Array<String>; then OtherClass would be aware that any usage of LOG_QUEUE compiles to this JS code OriginalClass.LOG_QUEUE
Alright, got a solution after discussing with Dima Granetchi from the Haxe experts group on Slack.
Now, although this will still generate the OtherClass that makes use of the OriginalClass's static members, you can cut down on the quantity of import statements for most (if not all) of the module/class's static members by using the wildcard * symbol, like in this example:
// OriginalClass.hx
package somePackage;
class OriginalClass {
public static var LOG_QUEUE:Array<String>;
public static function main() {
LOG_QUEUE = [];
OtherClass.doSomething();
}
public static function doSomethingOriginal() {
LOG_QUEUE.push("World!");
}
}
// OtherClass.hx
import somePackage.OriginalClass.*; // <-- Demonstrating the WILDCARD (*) symbol
class OtherClass {
public static function doSomething() {
LOG_QUEUE.push("Hello"); //Resolved to OriginalClass.LOG_QUEUE
doSomethingOriginal(); //Resolved to OriginalClass.doSomethingOriginal()
}
}
Although this is a minimal example, it becomes more useful when you have a few different dozen static members used in your OtherClass.
Note
TypeDefs defined in the OriginalClass used inside the OtherClass doesn't seem to get recognized/resolved (may be due to missing public accessor, but I was unable to set it on my typedefs). You can always import those specific TypeDefs with individual import statements, like so:
//Somewhere at the top of OtherClass.hx...
import somePackage.OriginalClass.MyTypeDef;