Meet An Upcoming Swift Access Modifier: package

Meet An Upcoming Swift Access Modifier: package

Coming in Swift 5.9, package will be a new access modifier in the language.

The Swift team at Apple approved the Swift Evaluation proposal SE-0386 which includes adding a new access modifier called package to allow symbols from being accessed inside the same package only.

Currently, there's only one way to access symbols throughout packages of a project and, it's by declaring them as public. However, sometimes you don't want a specific symbol to be accessed from a different module but only inside the same package. Right now, there's no way to make that restriction.

Fortunately, Swift Evaluation proposal SE-0386 adds the exact restriction to the language by adding a new access modifier package. All you have to do is to declare the symbols as package.

Suppose we have two modules and packages called Cat and Dog. If we declare a public struct called AnimalBehaviour inside the Cat package, all of its symbols will be accessible in the Dog package, as well as in the others throughout the project:

// Module: Cat, Package: Cat
public struct AnimalBehaviour {
    public func run() {}
    public func actNaughty() {}
}

public struct Cat {
    public let behaviour: AnimalBehaviour
}

let cat = Cat(behaviour: AnimalBehaviour())
cat.behaviour.run() // ✅ Accessible
cat.behaviour.actNaughty() // ✅ Accessible

// Module: Dog, Package: Dog
public struct Dog {
    public let behaviour: AnimalBehaviour
}

let dog = Dog(behaviour: AnimalBehaviour())
dog.behaviour.run() // ✅ Accessible
dog.behaviour.actNaughty() // ✅ Accessible

However, we want the actNaughty() to be accessible inside the module only that we declared AnimalBehaviour in, which is Cat. We simply declare the method as package:

// Module: Cat, Package: Cat
public struct AnimalBehaviour {
    public func run() {}
    package func actNaughty() {}
}

Now, It's possible to access actNaughty() inside the Car module:

let cat = Cat(behaviour: AnimalBehaviour())
cat.behaviour.run() // ✅ Accessible
cat.behaviour.actNaughty() // ✅ Accessible

However, for the Dog module, the compiler will not be able to find the intended symbol:

let dog = Dog(behaviour: AnimalBehaviour())
dog.behaviour.run() // ✅ Accessible
dog.behaviour.actNaughty() // ❌ Not accessible. Error: cannot find 'actNaughty()' in scope

Hopefully, this new access modifier helps large projects to be structured better.

Overall, this access modifier is pretty much similar to internal, except it has been made for Packages which are expressed by Swift Package Manager to prevent undesirable symbols by only allowing them to be shared within the same package.

It's also worth mentioning that package is more accessible than internal, fileprivate, and private. And less accessible than open and public. For example, a public function cannot use a package type in its parameters or return type, and a package function cannot use an internal type in its parameters or return type. Similarly, an @inlinable public function cannot use a package declaration in its implementation, and an @inlinable package function cannot use an internal declaration in its implementation.

Swift 5.9 was announced on March 6th of 2023. The release date hasn't been revealed yet, but it's expected to be released with the launch of iOS 17.