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.
Subscribe to my newsletter
Read articles from Makwan Barzan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Makwan Barzan
Makwan Barzan
iOS Engineer @ Gateway working on First Iraqi Bank.