Missing a level of access control on class/struct

This has been discussed in the past, with no particular conclusion, as far as I can tell. But I think the issue is ill-defined.

Imagine that we got a typeprivate access modifier that could allow for something like this:

// in file Foo1.swift

struct Foo {
  typeprivate var data: Data
}

extension Foo {
  mutating func frobulate() {
    // modify the `data` property 
  }
}

// in file Foo2.swift

extension Foo {
  mutating func frobnicate() {
    // modify the `data` property is allowed even if I'm in a separate file, because the access is `typeprivate`
  }
}

This will effectively make the data property internal, even if it doesn't look like it. That's because in any file within the module I could write something like this:

// in file SomewhereElseFarAwayInTheCodebase.swift

extension Foo {
  var internalDataProxy: Data {
    get {
      self.data
    }
    set {
      // this is fine because I'm in an extension of `Foo` and `data` is `typeprivate`
      self.data = newValue
    }
  }
}

struct DataBypassCaller {
  func mutateDataOn(foo: inout Foo) {
    let someNewData: Data = ...

    // the effect of this is like modifying the `data` property directly
    foo.internalDataProxy = someNewData
  }
}

Due to the fact that we can extend types in Swift, the file-level access control becomes very important.

Please note that, in Swift, this issue cannot even solved by classes or OOP, not even with a protected kind of access control: as long as you can access some private member in some way in some other file, you will always be able to create "proxy" members that break the encapsulation.

My suggestion is to simply put everything in the same file, and use organization tools like // MARK: - comments to highlight the specific sections. Alternatively, just use internal and make sure to have practices in place in your team to spot code that breaks invariants and/or encapsulation in code reviews. Finally, you can instead create a module (a SPM library target) that contains the files that share the data property, that would be declared as internal to the module: this way the property would not be public, thus, not visible from outside, but still accessible by the other files within the module.

Another thing that could help is a new access modifier that's not currently in Swift, but it could be introduced in the future, given that some people in the forums (myself included) have expressed interest in it, that is, folderprivate. If we could limit the visibility of a declaration to the files in a particular folder, we could break down large files into smaller ones, as long as they are in the same folder.

So, the example would change like the following:

// in file Foo1.swift

struct Foo {
  folderprivate var data: Data
}

extension Foo {
  mutating func frobulate() {
    // modify the `data` property 
  }
}

// in file Foo2.swift, in the same folder

extension Foo {
  mutating func frobnicate() {
    // modify the `data` property is allowed even if I'm in a separate file, because the access is `folderprivate`
  }
}

// in file SomewhereElseFarAwayInTheCodebase.swift, not in the same folder

extension Foo {
  var internalDataProxy: Data {
    get {
      self.data
    }
    set {
      // this will not compile, because the file is not in the same folder
      self.data = newValue
    }
  }
}

Even in this case, it would technically be possible to break encapsulation by sneaking in a new file in the folder, but this could be much more easily controlled, for example having specific owners defined for the files in that particular folder, that would be asked to approve any change in its files.