文章

iOS Info.plist与项目结构入门

Info.plist是iOS应用的配置核心,定义了应用标识、权限、界面方向等关键信息,理解其结构是iOS开发的基础。

iOS Info.plist与项目结构入门

一句话概括(面试开口第一句)

Info.plist是iOS应用的”配置中枢”,通过键值对定义应用唯一标识、版本号、权限需求、界面方向等关键信息,是Xcode项目结构的核心配置文件。

背景:为什么这个知识点重要

  • 应用身份标识CFBundleIdentifier是App Store和系统识别应用的唯一ID,不可重复
  • 版本管理CFBundleShortVersionString(营销版本)和CFBundleVersion(构建版本)共同决定应用更新逻辑
  • 权限声明:访问相机、相册、位置等敏感资源必须在Info.plist中声明使用目的
  • 设备兼容性:通过UIRequiredDeviceCapabilities等键控制应用支持的设备范围
  • 启动配置:启动图、主界面、支持的方向等影响用户体验的第一印象
  • 安全策略:ATS(App Transport Security)等安全配置在此定义

概念与定义

Info.plist(Information Property List)是iOS/macOS等Apple平台应用的核心配置文件,位于应用Bundle的根目录(iOS)或Contents目录(macOS)。它是一个属性列表(Property List)文件,采用XML或二进制格式,包含描述应用基本特征和行为的键值对。

Xcode项目结构中,Info.plist通常位于项目名/Supporting Files/目录下,文件名通常为项目名-Info.plist。在构建过程中,Xcode会将其编译并复制到最终应用的Bundle中。

属性列表(Property List)是Apple平台用于存储结构化数据的标准格式,支持String、Number、Boolean、Array、Dictionary等数据类型,可通过XML或二进制序列化。

最小示例(10秒看懂)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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>CFBundleIdentifier</key>
    <string>com.example.MyApp</string>
    
    <!-- 版本信息 -->
    <key>CFBundleShortVersionString</key>
    <string>1.0.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    
    <!-- 应用名称 -->
    <key>CFBundleDisplayName</key>
    <string>我的应用</string>
    
    <!-- 主界面 -->
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    
    <!-- 支持的方向 -->
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
    </array>
    
    <!-- 隐私权限声明 -->
    <key>NSCameraUsageDescription</key>
    <string>需要相机功能来拍摄照片</string>
</dict>
</plist>

核心知识点拆解(面试时能结构化输出)

1. 应用基本信息类键

  • CFBundleIdentifier:应用唯一标识,反向域名格式(如com.company.appname
  • CFBundleName:应用内部名称,默认与目标名称相同
  • CFBundleDisplayName:用户可见的应用名称(显示在Springboard)
  • CFBundleShortVersionString:营销版本号,用户可见(如"1.0.0"
  • CFBundleVersion:构建版本号,用于内部追踪(每次提交递增)

2. 界面与启动配置类键

  • UIMainStoryboardFile / UIMainStoryboardFile~ipad:主故事板文件
  • UILaunchStoryboardName:启动故事板(替代Launch Images)
  • UISupportedInterfaceOrientations:支持的界面方向
    • UIInterfaceOrientationPortrait(竖屏,Home键在下)
    • UIInterfaceOrientationLandscapeLeft(横屏,Home键在左)
    • UIInterfaceOrientationLandscapeRight(横屏,Home键在右)
    • UIInterfaceOrientationPortraitUpsideDown(竖屏倒置)

3. 隐私权限描述类键(iOS 10+强制)

  • NSCameraUsageDescription:相机使用描述
  • NSPhotoLibraryUsageDescription:相册使用描述
  • NSLocationWhenInUseUsageDescription:使用时定位描述
  • NSMicrophoneUsageDescription:麦克风使用描述
  • NSContactsUsageDescription:通讯录使用描述

4. 设备兼容性与特性类键

  • UIRequiredDeviceCapabilities:必需设备能力(如armv7opengles-2
  • UISupportedInterfaceOrientations~ipad:iPad专用方向配置
  • UISupportedInterfaceOrientations~iphone:iPhone专用方向配置

5. 安全与网络类键

  • NSAppTransportSecurity:应用传输安全配置(ATS)
  • NSExceptionDomains:ATS例外域名配置
  • NSAllowsArbitraryLoads:允许任意HTTP加载(慎用)

实战案例(2-3个)

案例1:完整的多平台应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?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>CFBundleIdentifier</key>
    <string>com.yourcompany.PhotoApp</string>
    
    <key>CFBundleName</key>
    <string>PhotoApp</string>
    
    <key>CFBundleDisplayName</key>
    <string>美图秀</string>
    
    <key>CFBundleShortVersionString</key>
    <string>2.3.1</string>
    
    <key>CFBundleVersion</key>
    <string>2026031501</string> <!-- 年月日+构建号 -->
    
    <!-- ========== 启动配置 ========== -->
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    
    <!-- ========== 界面方向(iOS 16+推荐配置) ========== -->
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
    </array>
    
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    
    <!-- ========== 隐私权限描述(必须提供) ========== -->
    <key>NSCameraUsageDescription</key>
    <string>需要使用相机拍摄照片和视频</string>
    
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要访问相册以选择照片进行编辑</string>
    
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>需要将编辑后的照片保存到相册</string>
    
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>需要获取当前位置以添加地理标签</string>
    
    <key>NSMicrophoneUsageDescription</key>
    <string>需要录制视频时的音频</string>
    
    <!-- ========== 设备兼容性 ========== -->
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>armv7</string> <!-- 支持ARMv7指令集 -->
    </array>
    
    <key>UISupportsPictureInPicture</key>
    <true/> <!-- 支持画中画功能 -->
    
    <!-- ========== 网络与安全 ========== -->
    <key>NSAppTransportSecurity</key>
    <dict>
        <!-- 允许加载HTTP资源(仅调试使用,上架需移除) -->
        <key>NSAllowsArbitraryLoads</key>
        <false/>
        
        <!-- 特定域名例外 -->
        <key>NSExceptionDomains</key>
        <dict>
            <key>insecure-api.example.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
        </dict>
    </dict>
    
    <!-- ========== 应用服务 ========== -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>photoapp</string> <!-- 自定义URL Scheme -->
            </array>
        </dict>
    </array>
    
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>whatsapp</string>
        <string>weixin</string> <!-- 声明可查询的URL Scheme -->
    </array>
    
    <!-- ========== 国际化 ========== -->
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string> <!-- 默认开发语言区域 -->
</dict>
</plist>

案例2:适配不同iOS版本的权限配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!-- 相机权限(所有iOS版本) -->
<key>NSCameraUsageDescription</key>
<string>需要相机拍摄照片</string>

<!-- 相册权限(iOS 14+ 细化) -->
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册选择照片</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要保存照片到相册</string>

<!-- 定位权限(按使用场景) -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要在应用使用时获取位置</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要始终获取位置(后台定位)</string>

<!-- 通知权限(iOS 12+ 细化) -->
<key>UNAuthorizationOptions</key>
<array>
    <string>alert</string>
    <string>sound</string>
    <string>badge</string>
</array>

<!-- 本地网络权限(iOS 14+) -->
<key>NSLocalNetworkUsageDescription</key>
<string>需要发现本地网络设备</string>
<key>NSBonjourServices</key>
<array>
    <string>_http._tcp</string>
    <string>_https._tcp</string>
</array>

案例3:网络安全配置(ATS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<key>NSAppTransportSecurity</key>
<dict>
    <!-- 推荐配置:严格安全策略 -->
    <key>NSAllowsArbitraryLoads</key>
    <false/> <!-- 禁止任意HTTP加载 -->
    
    <!-- 例外域名配置(必须明确理由) -->
    <key>NSExceptionDomains</key>
    <dict>
        <!-- 旧版API域名,无法升级HTTPS -->
        <key>legacy-api.example.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
        </dict>
        
        <!-- 第三方服务(如广告) -->
        <key>ads.thirdparty.com</key>
        <dict>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
    </dict>
    
    <!-- 全局TLS版本要求 -->
    <key>NSExceptionMinimumTLSVersion</key>
    <string>TLSv1.3</string>
</dict>

底层原理(精简、但关键)

1. Info.plist在应用Bundle中的位置与加载

1
2
3
4
5
6
应用Bundle结构:
MyApp.app/
  ├── Info.plist              ← 配置文件
  ├── MyApp                   ← 可执行文件
  ├── Base.lproj/             ← 基础资源
  └── ……

加载时机

  1. 安装时:系统验证Info.plist格式和必需键
  2. 启动时:系统读取CFBundleIdentifierCFBundleExecutable等键定位应用
  3. 运行时:通过[NSBundle mainBundle]infoDictionary访问配置

2. 隐私权限描述的系统级检查

1
应用请求权限 → 系统检查Info.plist对应描述 → 弹出系统对话框
  • iOS 10+强制要求:访问受保护资源必须在Info.plist提供UsageDescription
  • 缺失描述的后果应用会立即崩溃(crash with missing description)
  • 描述内容要求:必须具体、真实,App Store审核会验证合理性

3. 版本号更新逻辑

1
2
CFBundleShortVersionString:用户可见,可回滚
CFBundleVersion:内部追踪,必须单调递增
  • App Store更新判断:两个版本号任意一个增加即视为新版本
  • TestFlight限制CFBundleVersion必须递增才能上传新构建
  • 回滚场景:可降低CFBundleShortVersionString(如2.0→1.9),但CFBundleVersion仍需增加

4. 多设备方向支持的实现机制

1
2
3
4
5
6
7
8
9
// 系统读取UISupportedInterfaceOrientations数组
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    // 根据设备类型返回对应配置
    if (iPad) {
        return [infoDict[@"UISupportedInterfaceOrientations~ipad"] convertToMask];
    } else {
        return [infoDict[@"UISupportedInterfaceOrientations"] convertToMask];
    }
}
  • 优先级:设备专用配置 > 通用配置
  • 默认值:如果未指定,iPhone默认为UIInterfaceOrientationMaskAllButUpsideDown
  • 运行时变化:可通过代码动态修改支持方向

5. 属性列表的二进制格式优化

1
XML格式(可读) → 编译时转换为二进制格式(高效)
  • 存储效率:二进制格式比XML小30-50%
  • 解析速度:二进制解析速度比XML快5-10倍
  • 可逆转换:通过plutil命令可在XML/二进制间转换

💡 人话总结:Info.plist就像应用的”简历+说明书”,系统用它来确认你是谁(Bundle ID)、能做什么(权限)、擅长什么(设备兼容性)。隐私权限描述就像”申请理由”,必须充分合理才能通过系统审核。版本号管理如同”年级+学号”,营销版本可以回滚,但内部构建号必须一直增加。

高频面试题 + 回答模板

💬 面试回答话术

  1. 问:Info.plist是什么?包含哪些关键配置项?

    答:Info.plist是iOS应用的配置文件,位于应用Bundle中,采用属性列表格式存储应用的元数据。关键配置项包括:

    • 标识类CFBundleIdentifier(唯一包名)、CFBundleDisplayName(显示名称)
    • 版本类CFBundleShortVersionString(营销版本)、CFBundleVersion(构建版本)
    • 界面类UIMainStoryboardFile(主故事板)、UISupportedInterfaceOrientations(支持方向)
    • 权限类NSCameraUsageDescription等使用目的描述(iOS 10+强制)
    • 设备类UIRequiredDeviceCapabilities(必需硬件能力)

    这些配置影响应用安装、启动、权限申请和App Store审核。

  2. 问:CFBundleShortVersionString和CFBundleVersion有什么区别?

    答:这两个都是版本号,但用途不同

    • CFBundleShortVersionString用户可见的营销版本号,格式如"1.0.0"。用于App Store展示,可以回滚(如2.0→1.9)
    • CFBundleVersion内部构建版本号,格式为字符串但通常用数字,如"123"。用于TestFlight、系统更新判断,必须单调递增

    关键规则

    • App Store将两个版本号都视为更新依据:任意一个增加即视为新版本
    • TestFlight要求CFBundleVersion必须递增才能上传新构建
    • 回滚场景:可降低CFBundleShortVersionString,但CFBundleVersion仍需增加
  3. 问:iOS隐私权限描述(UsageDescription)有什么要求?缺失会怎样?

    答:iOS 10+强制要求应用在访问受保护资源前,必须在Info.plist提供对应的UsageDescription键值对。

    具体要求

    • 内容必须具体真实:描述为什么需要此权限,用户利益是什么
    • 禁止模板化描述:如”需要此功能”会被App Store拒绝
    • 明确场景:如”需要使用相机扫描二维码登录”

    缺失的后果

    • 应用会立即崩溃(crash with missing description)
    • App Store审核拒绝(必须修复才能上架)

    示例:<key>NSCameraUsageDescription</key><string>需要相机扫描二维码登录</string>

进阶与易错点

易错点1:隐私权限描述缺失导致崩溃

1
2
3
4
5
6
7
8
9
<!-- ❌ 错误:iOS 10+会崩溃 -->
<!-- 代码中调用相机,但Info.plist缺少描述 -->

<!-- ✅ 正确:必须提供具体描述 -->
<key>NSCameraUsageDescription</key>
<string>需要使用相机拍摄照片上传</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册选择头像图片</string>

易错点2:版本号管理混乱导致无法上传

1
2
3
4
5
6
7
8
9
10
11
<!-- ❌ 错误:CFBundleVersion未递增,TestFlight上传失败 -->
<key>CFBundleShortVersionString</key>
<string>1.2.0</string> <!-- 增加 -->
<key>CFBundleVersion</key>
<string>20260301</string> <!-- 未变,错误 -->

<!-- ✅ 正确:CFBundleVersion必须递增 -->
<key>CFBundleShortVersionString</key>
<string>1.2.0</string> <!-- 可以增加 -->
<key>CFBundleVersion</key>
<string>2026031501</string> <!-- 必须增加 -->

进阶技巧1:条件化配置(多Target)

1
2
3
4
5
6
<!-- 在主Info.plist中定义变量 -->
<key>APP_BUNDLE_ID</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

<!-- 在Target的Build Settings中覆盖 -->
PRODUCT_BUNDLE_IDENTIFIER = com.company.app.debug

进阶技巧2:动态读取配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 运行时读取Info.plist配置
class AppConfig {
    static var bundleIdentifier: String {
        return Bundle.main.bundleIdentifier ?? ""
    }
    
    static var versionString: String {
        return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
    }
    
    static func value<T>(forKey key: String) -> T? {
        return Bundle.main.infoDictionary?[key] as? T
    }
}

// 使用示例
let apiKey: String? = AppConfig.value(forKey: "API_KEY")

总结与记忆锚点

核心要点回顾

  1. 一个核心:Info.plist是iOS应用配置核心,定义身份、权限、兼容性
  2. 两个版本:CFBundleShortVersionString(用户可见) vs CFBundleVersion(内部递增)
  3. 三个必须:Bundle Identifier、版本信息、隐私权限描述(iOS 10+)
  4. 四个方向:Portrait、LandscapeLeft、LandscapeRight、PortraitUpsideDown
  5. 五类权限:相机、相册、定位、麦克风、通讯录等使用描述

一句话记忆类比

Info.plist是应用的”身份证+说明书+安全声明”:身份证(Bundle ID)证明身份,说明书(版本、方向)指导使用,安全声明(权限描述)告知风险与目的。

📋 快速自测(检验是否掌握)

  1. 用一句话解释Info.plist的作用
  2. 写出包含Bundle ID、版本号和相机权限描述的Info.plist片段
  3. 解释CFBundleShortVersionString和CFBundleVersion的区别及使用规则
  4. 描述缺失隐私权限描述(UsageDescription)的后果及正确做法

*文档生成时间:2026-04-02适配iOS 14+ (Xcode 15+) 规范基于Apple官方文档及上架审核要求整理*
本文由作者按照 CC BY 4.0 进行授权