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()
接口进行调用。