CocoaPods Package herb.svg

10.05.2025
4 min

Swift is evolving, and SE-0386 introduced the package access modifier for more granular control in Swift packages. While it seems designed for Swift Package Manager (SPM), limiting access within a package, it's not exclusive to it. Even projects using CocoaPods can benefit from it!

triangular_flag.png The -package-name Compilation Flag

If you read the proposal more closely, you'll notice that the package modifier is essentially tied to a compilation flag: -package-name. SPM automatically passes this flag with the name of the package during the build process. But here's the exciting part: this flag can be manually specified, even if you're not using SPM.

This insight opens up possibilities for using the package modifier in non-SPM setups, such as projects managed with CocoaPods. By explicitly setting the -package-name flag for multiple pods, you can simulate the behavior of a single package.

rocket.gif Using package in CocoaPods

Let's take an example where we have two pods: Saiga and WiseFields. We want to use the package modifier to share functionality between these pods while keeping the rest of the code encapsulated.

Defining a Package-Scoped Function

In Saiga, we define a function using the package modifier:

package func helloWorld() {
print("Hello world!")
}

This function is accessible only within the same package, as defined by the -package-name flag.

Configuring CocoaPods Specs

To ensure that Saiga and WiseFields are treated as part of the same package, we need to include the ‑package-name flag in their .podspec files. Here's how it looks:

Saiga.podspec

Pod::Spec.new do |s|
s.name = "Saiga"
s.version = '1.0.0'
s.summary = "none"
s.homepage = "none"
s.author = "SwiftyFinch"
s.source = { :path => "*" }
s.ios.deployment_target = '16.0'
s.static_framework = true
s.prefix_header_file = false
s.source_files = "#{s.name}/Sources/**/*.swift"
s.pod_target_xcconfig = {
"OTHER_SWIFT_FLAGS" => "-package-name Core"
}
end

WiseFields.podspec

Pod::Spec.new do |s|
s.name = "WiseFields"
s.version = '1.0.0'
s.summary = "none"
s.homepage = "none"
s.author = "SwiftyFinch"
s.source = { :path => "*" }
s.ios.deployment_target = '16.0'
s.static_framework = true
s.prefix_header_file = false
s.source_files = "#{s.name}/Sources/**/*.swift"
s.pod_target_xcconfig = {
"OTHER_SWIFT_FLAGS" => "-package-name Core"
}
s.dependency "Saiga"
end

By setting -package-name Core in both podspecs, we ensure that Saiga and WiseFields belong to the same logical package.

party.png Accessing the helloWorld()

Now that both pods are part of the same package, we can call the package-scoped function from Saiga:

import Saiga
helloWorld() // Output: Hello world!

The function helloWorld is accessible in WiseFields because the -package-name flag unifies these modules under the same package name, even though they are managed by CocoaPods.

toolbox.svg Why This Matters

  • The package access control modifier is a critical feature for managing core functionality across multiple modules.
  • It enables developers to encapsulate experimental or unstable APIs within package boundaries, ensuring they are not prematurely exposed.
  • This approach minimizes compatibility risks and provides the flexibility to refine functionality until it's ready for broader use.

finish.svg Conclusion

The package modifier is a versatile tool that enhances modularity and API management for both SPM and CocoaPods users. By enabling safe API iteration and reducing exposure risks, it simplifies the development of robust, maintainable code. Swift's continued evolution empowers developers to build better workflows across diverse ecosystems.

You can explore this in action in the example project.