惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
Jina AI
Jina AI
aimingoo的专栏
aimingoo的专栏
I
InfoQ
IT之家
IT之家
罗磊的独立博客
Blog — PlanetScale
Blog — PlanetScale
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog RSS Feed
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
博客园_首页
Engineering at Meta
Engineering at Meta
Martin Fowler
Martin Fowler

肘子的 Swift 记事本 | Fatbobman's Blog

从 pbxproj 到 xcproj:Xcode 工程配置迎来 JSON 格式 iPhone Duo 带来的机遇与挑战 -- 肘子的 Swift 周报 #153 当 Mac mini 的价格不再 mini -- 肘子的 Swift 周报 #152 SwiftData:优化从建模开始 举手之劳 -- 肘子的 Swift 周报 #151 从使用 AI 到委托 AI(下):我想要的可委托性 AI 想放弃了,人没有 -- 肘子的 Swift 周报 #150 从使用 AI 到委托 AI:我的一些思考 越来越慢的 App Review,拦住了谁? -- 肘子的 Swift 周报 #149 ContentBuilder 解析:SwiftUI 类型检查性能提升的秘密 Apple Intelligence 已通过中国政府审核,即将在中国提供服务 -- 肘子的 Swift 周报 #148 在 SwiftUI Text 中控制孤行:挖掘未公开的 avoidsOrphans 热茶还是冰咖啡 -- 肘子的 Swift 周报 #147 Liquid Glass:UIKit 适配踩坑实录 不是模型变慢了,是任务变大了 -- 肘子的 Swift 周报 #146 当灵感跑在了结果前面 -- 肘子的 Swift 周报 #145 当每一次写入都有了新价格 -- 肘子的 Swift 周报 #144 当 Linux 成为“空气”:容器、Agent 与不再重要的“桌面之争” -- 肘子的 Swift 周报 #143 两个 SwiftUI 动画 Bug 排查小记 SPI 加入 Apple,Swift 迈向自举 -- 肘子的 Swift 周报 #142 Swift 还让你 Excited 吗? -- 肘子的 Swift 周报 #141 从 Size Class 到可用空间,horizontalSizeClass 还可靠吗? WWDC 26:AI 帮你看完了,然后呢? -- 肘子的 Swift 周报 #140 WWDC 2026 初印象:符合预期,但更务实 -- 肘子的 Swift 周报 #139 Core Data + Observation:从属性级响应到心智解放 稳定 > 新功能 -- 肘子的 Swift 周报 #138 用自定义 Layout 化解 SwiftUI List 的行高与间距跳变 从社区路标到生态基石:Dave Verwer 的新篇章 -- 肘子的 Swift 周报 #137 消失的 WWDC 愿望单 -- 肘子的 Swift 周报 #136 CocoaPods 正在退场,SwiftPM 才刚到第二章 -- 肘子的 Swift 周报 #135
Swift 模块化:使用 package 关键字实现 Target 间安全共享
东坡肘子 · 2025-05-08 · via 肘子的 Swift 记事本 | Fatbobman's Blog

Read in English

🔋

(更新于 )

核心摘要: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() 接口进行调用。

延伸阅读

相关提示

订阅 Fatbobman 周报

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

立即订阅