A new access modifier in Swift: package

A new access modifier in Swift: package

Β·

3 min read

In this blog post, I'll explain when and where you can use Swift's new package access modifier. I'll also give an outlook on plans from Apple to extend its usefulness for closed-code enterprise SDKs.

Recap: access modifiers you already know

Up until 5.8, there were five different access levels for entities within Swift code.

  • open

  • public

  • internal (a.k.a the default)

  • private

  • fileprivate

New in Swift 5.9: package

Apple added a sixth access level, package, in Swift 5.9 bundled with Xcode 15.

Why is it helpful for package developers

When using the Swift Package Manager, you can use this new contextual modifier to share code between modules within your package without sharing the code outside your package.

Let's assume I want to develop a Swift package with two modules named Essentials and Utilities.

RequirementSolution
to make the module Utilities accessible outside of this packagedeclare Utilities also as a library product
allow code reuse of module Utilities in Essentials while preventing the use of some Utilities code outside of this package.use the package access modifier in the Utilities module
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "SampleSDK",
    products: [
        .library(
            name: "Essentials",
            targets: ["Essentials"]
        ),
        .library(
            name: "Utilities",
            targets: ["Utilities"]
        ),
    ],
    targets: [
        .target(
            name: "Essentials",
            dependencies: ["Utilities"]
        ),
        .target(
            name: "Utilities"
        ),
    ]
)

Any types declared with package in the Utilities module, e.g.

package func trace()

can be accessed from the Essentials module

// this code is part of the `Essentials` module

import Utilities

trace()

but not from developers using that package

// this code is outside of the package

import Utilities

trace() // ERROR !!!!

In the package manifest, you can opt-out of the default behavior

// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "SampleSDK",
    products: [
        .library(
            name: "Essentials",
            targets: ["Essentials"]
        ),
        .library(
            name: "Utilities",
            targets: ["Utilities"]
        ),
    ],
    targets: [
        .target(
            name: "Essentials",
            dependencies: ["Utilities"],
            packageAccess: false // <== opt-out !!!
        ),
        .target(
            name: "Utilities"
        ),
    ]
)

If you are interested in more details for this new modifier, especially on subclassing and overrides, then you can find such information in the respective Swift evolution proposal document SE-0386.

Future: Applicability for binary frameworks

Closed-code SDKs often distribute multiple binary frameworks, so it would be great if SDK developers could share code between those frameworks without exposing that code to consumers.

Frameworks of an SDK would need to share the same package name. Looking at the build settings of a framework, you can find a Package Access Identifier for exactly that use case 😊

However, the Swift compiler only supports this concept for packages built from source and otherwise will throw such an error for a binary framework:

Module 'xyz' is in package 'us.eidinger.MySDK' but was built from interface; modules of the same package can only be loaded if built from source

If Swift can generate an additional .private.swiftinterface file for System Programming Interfaces, then it could also create a .package.swiftinterface file. And that's the plan from Apple. I found this promising addition in Apple's Swift main code line to support loading a package interface.

I am looking forward to a future Swift version that allows SDK developers to support the package access modifier in binary frameworks.

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!