TL;DR: Swift 5.9 引入
package访问控制修饰符,用于将 API 访问范围限定在同一 Package 内(含多个 Target),避免使用public造成过度暴露。此功能特别适用于模块化结构中需要在多个 Target 之间共享实现但不希望对外开放的场景,提升封装性和安全性。
背景
随着模块化编程的普及,开发者越来越倾向于通过 Swift Package Manager(SPM)将不同功能拆分为多个 Package。进一步地,同一个 Package 又常被细分为多个 Target。在这种结构中,如何限制 API 仅在 Package 内部(包括不同 Target)使用,同时避免使用 public 将其暴露给外部,是一个常见的需求。
package 关键字的引入
自 Swift 5.9 起,Swift 引入了新的访问控制修饰符 package,用于指定 API 的可见范围仅限于同一个 Package 内的不同 Target,从而无需将内部实现暴露给外部模块。
public struct MyStruct {
public init() { ... }
public var name: String { ... }
package func action() { ... } // 仅在同一个 Package 中可访问
}实际应用场景
以某项目中的 Persistent Package 为例,该 Package 处理所有数据持久化相关功能,并拆分为多个 Target:
Project
├── Domain
│ └── Package.swift
└── Persistent
├── Package.swift
└── Sources
├── Models (Target)
├── CURD (Target)
└── Stack (Target)在 Domain Package 中,定义了线程安全的 ViewModel 转换协议:
public protocol ViewModelConvertible {
associatedtype Value: ViewModelProtocol
@MainActor
func convertToViewModel() -> Value
}由于 NSManagedObject 并非线程安全,需在 Persistent Package 内部以不同方式处理 ViewModel 的转换,无法使用 @MainActor 限定。因此可以在 Persistent 的任意 Target 中定义如下协议:
package protocol ViewModelConvertibleUnsafe {
associatedtype Value: ViewModelProtocol
func convertToViewModelUnsafe() -> Value
}通过使用 package 修饰,该协议仅在 Persistent Package 内部的 Target 间共享,确保内部逻辑的灵活性与外部接口的安全性。外部模块依旧通过线程安全的 convertToViewModel() 接口进行调用。
延伸阅读
"加入我们的 Discord 社区,与超过 2000 名苹果生态的中文开发者一起交流!"