核心摘要:Swift 的 package 访问修饰符允许开发者在同一个 Package 内的多个 Target 之间共享 API,同时对 Package 外部保持不可见。它有效解决了模块化开发中 internal 权限过窄、public 权限过宽的痛点,是构建复杂 SPM 依赖树的标准实践。
Swift 5.9 引入的 package 访问控制修饰符,旨在将 API 访问范围限定在同一 Package 内(含多个 Target),避免使用 public 造成过度暴露。此功能特别适用于模块化结构中需要在多个 Target 之间共享实现但不希望对外开放的场景,提升封装性和安全性。
背景
随着模块化编程的普及,开发者越来越倾向于通过 Swift Package Manager(SPM)将不同功能拆分为多个 Package。进一步地,同一个 Package 又常被细分为多个 Target。在这种结构中,如何限制 API 仅在 Package 内部(包括不同 Target)使用,同时避免使用 public 将其暴露给外部,是一个常见的需求。
package 关键字的引入
package 关键字指定 API 的可见范围仅限于同一个 Package 内的不同 Target,从而无需将内部实现暴露给外部模块。这填补了 internal(仅模块内可见)和 public(完全公开)之间的空白。
public struct MyStruct {
public init() { ... }
public var name: String { ... }
// 仅在同一个 Package 内的其他 Target 中可访问,外部不可见
package func action() { ... }
}
实际应用场景
以某项目中的 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 (Core Data) 并非线程安全,在 Persistent Package 内部处理 ViewModel 转换时,可能无法直接满足 @MainActor 的限制。因此可以在 Persistent 的任意 Target 中定义如下协议:
package protocol ViewModelConvertibleUnsafe {
associatedtype Value: ViewModelProtocol
// 仅供 Package 内部使用的非安全接口
func convertToViewModelUnsafe() -> Value
}
通过使用 package 修饰,该协议仅在 Persistent Package 内部的 Target 间共享,确保内部逻辑的灵活性与外部接口的安全性。外部模块依旧通过线程安全的 convertToViewModel() 接口进行调用。