🔋

Swift 模块化:使用 package 关键字实现 Target 间安全共享

(更新于 )

核心摘要: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(完全公开)之间的空白。

Swift
public struct MyStruct {
    public init() { ... }
    public var name: String { ... }
    
    // 仅在同一个 Package 内的其他 Target 中可访问,外部不可见
    package func action() { ... } 
}

实际应用场景

以某项目中的 Persistent Package 为例,该 Package 处理所有数据持久化相关功能,并拆分为多个 Target:

Text
Project
├── Domain
│   └── Package.swift
└── Persistent
    ├── Package.swift
    └── Sources
        ├── Models       (Target)
        ├── CURD         (Target)
        └── Stack        (Target)

Domain Package 中,定义了线程安全的 ViewModel 转换协议:

Swift
public protocol ViewModelConvertible {
    associatedtype Value: ViewModelProtocol
    
    @MainActor
    func convertToViewModel() -> Value
}

由于 NSManagedObject (Core Data) 并非线程安全,在 Persistent Package 内部处理 ViewModel 转换时,可能无法直接满足 @MainActor 的限制。因此可以在 Persistent 的任意 Target 中定义如下协议:

Swift
package protocol ViewModelConvertibleUnsafe {
    associatedtype Value: ViewModelProtocol
    
    // 仅供 Package 内部使用的非安全接口
    func convertToViewModelUnsafe() -> Value
}

通过使用 package 修饰,该协议仅在 Persistent Package 内部的 Target 间共享,确保内部逻辑的灵活性与外部接口的安全性。外部模块依旧通过线程安全的 convertToViewModel() 接口进行调用。

延伸阅读

相关提示

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅