
iOS .entitlements 文件的作用,是声明某个 Target 希望获得哪些受代码签名保护的系统能力。但它不是一张可以自行填写的授权书。真正生效的是写入 App 可执行文件代码签名中的 Entitlements,而且必须落在 Apple 为该 App 授权的范围内。
如果只记住一句话,可以记住这个关系:
本地声明 + Apple 授权 + 开发者签名 + 系统验证 = 最终可用的系统能力
这篇文章会把 .entitlements、App ID、Provisioning Profile、Code Signing 和 iOS 运行时校验放到同一条安全链路里讲清楚,并给出一套从构建产物反查问题的方法。
iOS .entitlements 文件到底有什么作用
.entitlements 是一个 Property List 文件。它记录的是当前 Target 在构建时希望声明的特殊能力,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.ink.jinghai.markzen</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.ink.jinghai.markzen</string>
</array>
</dict>
</plist>
这三个键分别对应:
- •
aps-environment:使用 APNs 推送服务的环境。 - •
com.apple.developer.icloud-container-identifiers:允许访问的 iCloud Container。 - •
com.apple.security.application-groups:主 App 与 Extension 可共享的 App Group。
Apple 对 entitlement 的定义很明确:它是赋予可执行文件使用某项服务或技术的权利,最终以键值对形式嵌入二进制的代码签名。项目里的 .entitlements 文件只是 Xcode 计算最终结果时的一份输入。
这意味着,手动向文件里加入一个键,并不会让 App 自动获得对应能力。例如:
<key>com.apple.developer.healthkit</key>
<true/>
任何人都能写出这段 XML,但系统不会因为本地文件里有这个键,就允许 App 使用 HealthKit。它还要经过 App ID、Provisioning Profile、代码签名和系统校验。
五个角色,不是三份重复配置
Entitlements 相关问题难懂,通常不是因为 XML,而是几个相似名词承担了不同职责。
| | |
|---|
.entitlements | | |
| 这个 Team 名下的 Bundle ID 可以配置什么能力 | |
| Apple 授权谁、哪个 App、在什么条件下运行,以及可声明哪些 Entitlements | |
| | |
| | |
更准确的结构不是单向复制,而是本地声明与 Apple 授权在签名阶段汇合:
项目 .entitlements
│
│ 声明当前 Target 需要什么
▼
最终代码签名中的 Entitlements
▲
│ Xcode / codesign 结合项目与账号信息生成
│
Provisioning Profile
▲
│ Apple 根据 App ID、证书、设备与能力签发
│
App ID Capabilities

Apple 的 TN3125 将 Provisioning Profile 中的 Entitlements 描述为允许列表。App 真正声明某个 entitlement,则要把它放进自身代码签名。
可以把两者的关系概念化为:
App 代码签名实际声明的权限
必须落在
Provisioning Profile 允许的范围内
但不要把它实现成简单的文本比较。Profile 可能使用通配符,签名阶段会把某些值展开成完整的 Team ID、Bundle ID 或容器标识;不同 entitlement 也可能有各自的匹配规则。排查时要理解允许关系,同时比较实际键和值,而不是只比较两段 XML 是否逐字相同。
从 Xcode 到 iOS,权限是怎样生效的
第一步:Target 声明能力
在 Xcode 中打开:
Target
→ Signing & Capabilities
→ + Capability
添加 iCloud、Push Notifications、App Groups 或 Associated Domains 后,Xcode 可能会:
- 1. 创建或修改
.entitlements 文件。 - 2. 修改
Info.plist 或链接相关 Framework。 - 3. 在开发者账号中配置对应的 App ID 服务。
- 4. 创建或更新 Provisioning Profile。
Apple 也明确提醒:并非每个 Capability 都一定对应一个 entitlement。不要根据功能名称猜键名,更不要从网络文章里复制一个看似合理的 com.apple.developer.* 键。
第二步:Apple 签发 Provisioning Profile
Provisioning Profile 不是普通 plist。它是被 Cryptographic Message Syntax 签名的数据,其中会关联:
- • Development 或 Ad Hoc Profile 中允许的设备。
- • Apple 授权的 Entitlements。
因为 Profile 由 Apple 加密签名,设备才能把它当成授权依据,而不是相信开发者本地任意创建的文件。
第三步:代码签名写入最终 Entitlements
构建 App 时,Xcode 会结合:
- • Target 和 Build Settings。
- • 选中的 Provisioning Profile。
- • Team ID、Bundle ID 等签名上下文。
计算出最终 Entitlements,并把它们写入可执行文件的代码签名。
因此,下面三份数据可能相关,但不保证完全相同:
源码中的 App.entitlements
Provisioning Profile 中的 Entitlements
App 代码签名中的最终 Entitlements
真正代表当前构建产物实际声明的是第三份。
第四步:系统在安装、启动和服务调用时校验
在开发、Ad Hoc 等需要 Profile 的分发场景中,设备会检查代码签名、Profile、证书、App 标识、有效期、设备范围和 Entitlements 是否满足要求。签名或权限不合法时,安装或启动会直接失败,而不是由系统静默删除超出的权限。
App 启动后,相关系统服务还会继续基于进程的签名身份实施访问控制。例如:
import CloudKit
let container = CKContainer(
identifier: "iCloud.ink.jinghai.markzen"
)
CloudKit 不会只相信传入的字符串。系统还会检查当前进程签名中的 iCloud entitlement,确认它是否允许访问这个容器。
这里还有一个容易被简化掉的边界:App Store 会在分发流程中检查并重新签名 App。TN3125 说明,最终从 App Store 下载的 App 不一定继续携带开发者构建时的 Provisioning Profile。所以上面的“设备比较签名与内嵌 Profile”最适合解释开发、Ad Hoc 和安装排错,不能无条件套用到所有最终分发包。
.entitlements、Info.plist 和用户授权不是一回事
这三层经常被混在一起:
| | |
|---|
Info.plist | NSCameraUsageDescription | App 的元数据、运行配置,以及向用户解释为何访问隐私资源 |
| iCloud、App Groups、Associated Domains | |
| | |
例如远程通知至少涉及两件不同的事:
- 1. App 的签名需要包含有效的
aps-environment entitlement,才能使用 APNs 能力。 - 2. App 要显示提醒、播放声音或更新角标,还需要通过
UNUserNotificationCenter 请求用户授权。
可以使用现代 Swift 并发接口:
import UserNotifications
func requestNotificationAuthorization() async throws -> Bool {
let center = UNUserNotificationCenter.current()
// 只申请产品确实会使用的通知交互类型
return try await center.requestAuthorization(
options: [.alert, .sound, .badge]
)
}
Entitlement 通过,不代表用户一定同意;用户同意通知,也不能替代签名层的 APNs 配置。
常见 Entitlements 与使用场景
iCloud 和 CloudKit
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.example.app</string>
</array>
它通常用于 CloudKit、iCloud Documents,以及采用 CloudKit 同步的 Core Data 或 SwiftData 场景。容器标识需要与 App ID 配置和 Profile 授权一致。
Push Notifications
<key>aps-environment</key>
<string>development</string>
发布签名下通常会得到 production。不建议通过维护两份手写值来切换环境,应让签名和 Provisioning Profile 生成正确结果,再从最终产物验证。
App Groups
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.app</string>
</array>
它常用于主 App 与 Widget、Share Extension、Notification Service Extension 等共享容器。
import Foundation
let sharedDefaults = UserDefaults(
suiteName: "group.com.example.app"
)
sharedDefaults?.set(
true,
forKey: "hasCompletedOnboarding"
)
主 App 和 Extension 是不同的可执行目标。两边都要配置正确的 App Group,并分别检查各自的签名 Entitlements。
Associated Domains
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
<string>webcredentials:example.com</string>
</array>
它可用于 Universal Links、Shared Web Credentials 等能力。签名正确只是其中一层,网站侧的关联文件和域名配置也必须正确。
Sign in with Apple
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
除本地 Target 外,还要确认 App ID 和服务端使用的标识配置一致。
Keychain Sharing
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.example.shared</string>
</array>
这里尤其要注意 App ID Prefix。历史账号、App 转移、多 Target 和多团队项目中,Prefix 不匹配可能导致安装失败或升级后无法继续访问原有 Keychain 数据。
一次理解偏差:把授权链路看成复制链
我在梳理 iCloud.ink.jinghai.markzen 这个容器配置时,最初也把关系画成了:
.entitlements
↓
App ID Capabilities
↓
Provisioning Profile
这个画法看起来直观,却容易让人误以为同一份配置只是复制了三遍。它还会带来一个直接的排查错误:只盯着源码中的 .entitlements,看到容器字符串存在,就认为权限已经生效。
后来把结构改成“本地声明与 Apple 授权在代码签名处汇合”,排查顺序也随之改变:
先看当前 Target 使用哪个 entitlement 文件
↓
再看构建产物实际签进了什么
↓
然后看 Profile 允许什么
↓
最后核对 App ID、Team、容器和分发环境
这个转变是这次梳理里最重要的教训:源码只能说明意图,产物才能说明结果。
四条命令,检查真正生效的 Entitlements
1. 查看 Target 使用哪个文件
xcodebuild \
-showBuildSettings \
-scheme YourScheme |
grep CODE_SIGN_ENTITLEMENTS
如果项目为 Debug 和 Release 配置了不同文件,这一步可以发现当前 Configuration 实际引用哪一个路径。
2. 查看 App 代码签名中的最终 Entitlements
codesign -d --entitlements :- \
"/path/to/YourApp.app"
部分较新的 codesign 版本也接受:
codesign -d --entitlements - \
"/path/to/YourApp.app"
codesign 的诊断信息可能写到标准错误流。在脚本里需要捕获输出时,可以显式合并:
codesign -d --entitlements :- \
"/path/to/YourApp.app" 2>&1
3. 解码内嵌 Provisioning Profile
security cms -D \
-i "/path/to/YourApp.app/embedded.mobileprovision"
4. 只提取 Profile 的 Entitlements
security cms -D \
-i "/path/to/YourApp.app/embedded.mobileprovision" |
plutil -extract Entitlements xml1 -o - -
最终应该比较的是:
codesign 输出的 App 实际声明
对照
Profile 中 Apple 给出的允许范围

如果你只检查项目文件,会漏掉这些问题:
- • Build Setting 指向了另一份
.entitlements。 - • Debug 与 Release 使用了不同配置。
- • Archive 选择了不同的 Team 或 Profile。
- • Profile 没有在 App ID 能力变更后更新。
- • 主 App 正确,但 Extension 的签名配置错误。
invalid entitlements 应该怎样定位
构建时报 Profile 不包含某个 entitlement
常见信息类似:
Provisioning profile doesn't include the
com.apple.developer.xxx entitlement
按这个顺序检查:
- 1. 该 entitlement 是否真实存在,当前平台和账号是否有资格使用。
- 2. Xcode 的 Signing & Capabilities 是否已添加对应能力。
- 3. Apple Developer 后台的 App ID 是否启用了对应服务。
- 4. App ID 或证书变更后,Profile 是否重新生成。
- 5. 当前 Configuration 是否选中了更新后的 Profile。
如果它属于 Apple 审批的 Managed Capability,还要先确认账号已经获得授权。仅在本地写键无法绕过审批。
安装或启动时报签名权限无效
常见信息包括:
The executable was signed with invalid entitlements
或:
entitlement has value not permitted by a provisioning profile
这时不要先删除 DerivedData 反复重试。优先导出 codesign 与 Profile 的 Entitlements,核对:
- • Team Identifier 与 App ID Prefix
清缓存只能解决本地选择了旧资产一类问题,不能修复 App ID 或 Profile 本身没有授权。
安装成功,但系统服务仍然拒绝
这种情况说明问题可能已经从“签名能否通过”进入“服务配置是否完整”:
- • CloudKit Container 是否属于当前 Team。
- • Associated Domains 的网站文件是否可访问且内容匹配。
- • App Group 是否同时配置在主 App 与相关 Extension。
Entitlements 是必要条件,但不保证服务端配置、用户授权和业务代码也正确。
实际项目中的七条建议
- 1. 优先通过 Signing & Capabilities 添加能力。除非官方文档要求,不要凭键名手写。
- 2. 把
.entitlements 提交到 Git。它是项目配置的一部分,但不要复制其他项目的 Team、App Group 或 iCloud Container。 - 3. 每个可执行 Target 分开检查。App、Widget、Share Extension 都有自己的签名和权限边界。
- 4. 区分 Debug、Release 和 Archive。真机开发包能用,不代表发布包的 Entitlements 一定相同。
- 5. App ID、证书或能力变化后更新 Profile。手动签名项目尤其要注意旧 Profile。
- 6. 把 Archive 产物当成最终证据。提交前检查实际签名,不要只看 Xcode UI 是否显示绿色。
- 7. Simulator 不能替代真机签名验证。Apple 的 TN2415 指出,Simulator 构建不经过与设备构建相同的代码签名流程。
总结
.entitlements 最容易被误解成“权限配置文件”,但更准确的说法是“代码签名权限的项目级声明输入”。
完整链路是:
.entitlements 声明 Target 想要什么
App ID Capabilities 记录账号侧可配置的能力
Provisioning Profile 提供 Apple 签名的授权范围
Code Signing 把最终权限、身份和二进制绑定
iOS 在安装、启动和服务调用时实施校验
遇到问题时,不要止步于“文件里已经写了”。从 codesign 输出和 Profile 的 Entitlements 开始比较,通常能更快找到真正不一致的那一层。
如果这篇文章对你有帮助,欢迎点个在看,或者分享给正在处理 iOS 签名问题的朋友。
关注沐风iOS,不定期更新,全是干货。
2026.07.29 18:15
沪 · 赵巷
📌 声明:本文由 AI 辅助完成