skip to Main Content

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?

2

Answers


  1. private at file scope is equivalent to fileprivate.

    @testable import makes internal code accessible to a test target.

    Login or Signup to reply.
  2. 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).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search