[Pitch] `folderprivate` Access Control Keyword

Hello Swift Community,

I'd like to pitch a new access control keyword: folderprivate.

Introduction

folderprivate would restrict access to properties, methods, and objects to the same folder or inner folders, filling a gap between fileprivate and internal.

Motivation

As Swift projects grow, especially with SwiftUI, files can become unwieldy. Extracting views and properties into separate files improves maintainability, but current access levels are either too restrictive or too permissive. folderprivate aims to solve this by promoting modular design and better organization.

Proposed Solution

Introduce the folderprivate keyword to restrict access to the same folder and its inner folders, enhancing code encapsulation and organization.

Detailed Design

  • Syntax: folderprivate can be applied to classes, structs, enums, properties, and methods.
    folderprivate struct FolderPrivateStruct {
        folderprivate var folderPrivateProperty: String = "Accessible within the folder"
        
        folderprivate func folderPrivateMethod() {
            print("This method is private within the folder")
        }
    }
    
  • Semantics:
    • Folder-Level Access: Members marked with folderprivate can only be accessed within the same folder or any subfolders.
    • Inheritance: Subclasses in the same folder or subfolders can access folderprivate members of their superclass.
  • Compiler Enforcement: The compiler will enforce this by checking the directory structure.
  • Integration with Build Systems: Build systems need to recognize and respect folder boundaries for folderprivate access control.

Examples

Consider a scenario where a main view has multiple child views that should not be exposed outside of the folder. Only the main view should be accessible outside the folder.

// File: /Views/MainView/MainView.swift
struct MainView: View {
    var body: some View {
        VStack {
            HeaderView()
            ContentView()
            FooterView()
        }
    }
}

// File: /Views/MainView/HeaderView.swift
folderprivate struct HeaderView: View {
    var body: some View {
        Text("Header")
    }
}

// File: /Views/MainView/ContentView.swift
folderprivate struct ContentView: View {
    var body: some View {
        Text("Content")
    }
}

// File: /Views/MainView/FooterView.swift
folderprivate struct FooterView: View {
    var body: some View {
        Text("Footer")
    }
}

In this example, HeaderView, ContentView, and FooterView are only accessible within the /Views/MainView folder, ensuring encapsulation and preventing unintended usage outside this folder.

Impact on Existing Code

This is purely additive and does not impact existing code. Current access control levels remain unchanged and fully functional.

Alternatives Considered

  • Folder-Level Modules: Significant restructuring and build system changes.
  • File-level Extensions: Over-segmentation and does not provide the desired encapsulation within a folder.

Future Directions

If well-received, further refinements could include more granular access control keywords or hierarchical access controls.

Conclusion

The folderprivate keyword enhances Swift by providing a new level of encapsulation, aligning with the organizational needs of modern Swift projects. It promotes more modular, maintainable, and readable codebases.

Looking forward to your feedback and thoughts!

1 Like

I don’t particularly like anything that ties code to file layout, but I’d take almost anything that would allow me to hide things with class or protocol hierarchies.

9 Likes

Your example doesn't seem to match your description. HeaderView should only be accessible from its same folder or subfolders, namely Components. But MainView isn't in Components or a subfolder so shouldn't have access.

Your example should look more like:

// File: /Views/MainView/MainView.swift
// File: /Views/MainView/HeaderView.swift
// File: /Views/MainView/ContentView.swift
// File: /Views/MainView/FooterView.swift

Honestly this doesn't excite me. The existing access control keywords relate to types not files, except for fileprivate (which I endeavor to never use). Some way to create a module, like a Java package, without too much ceremony, would be better than this, and allow the same access control.

Agreed. I find Rust's access control tied to custom nested scopes quite appealing.

Thank you for pointing that out! I will update the code examples accordingly.

The examples were meant to show that MainView is accessible from other folders because it defaults to internal without an explicit access control keyword. In contrast, views marked with folderprivate are restricted to their own folder. This helps avoid having large files with all views and objects marked as fileprivate or private within the same file.

Strongly -1.

  1. I don’t think we have to introduce any more visibility modifiers. While all of them have a perfect sense of the use case, there are still too much of them already. No need to complicate visibility.
  2. Swift Packages don’t have the notion of the folders within the target/module (and Xcode just few magnitudes worse in that matter). So folder privacy just have too few sense there. If you want to separate code, you can create separate modules (which will be in different folders), and add them as dependencies to main project in case of Xcode (which will also helps to reduce the size of project file, as most of your code will move to the packages).
4 Likes

I'd agree that the file structure should not become a language feature since just moving file around may change visibility and potentially have undesirable side effects on functionality, e.g. the wrong HeaderView exposed to your MainView as a result of moving files being probably the most innocent possible side effect.

I'd suggest instead to introduce a slight modification to internal:

internal(MainView) struct HeaderView: View {
    var body: some View {
        Text("Header")
    }
}

This would limit the visibility of HeaderView to the MainView struct.

(Edit: not sure internal is the best candidate, might as well be private)

At that point, i'd restructure my code to be able to move all the stuff in that folder, to a seperate target.
This has it's own limitations, but for me personally, the code is usually enough self-contained to be fine with moving to a new target.

One general problem is that the compiler or SwiftPM currently basically don't care about the folder of files at all, to my understanding. That's in contrast to the fact that there are stuff that are validated or done at a file-by-file level.