构建轻量快速的 App Clips
Build light and fast App Clips
2021年6月10日
一句话判断
10MB 限制不是建议,是生死线——如果你的 App Clip 超过这个大小,用户在蜂窝网络下的体验会灾难性地差,这场 Session 教你怎么把体积压到极限。
这场 Session 讲了什么
App Clips 的核心卖点就是「快」:用户扫码/碰 NFC 后,几秒钟内就能用上你的服务。但「快」的前提是包体足够小。iOS 15 中 App Clip 的最大限制仍然是 10MB(展开后),这场 Session 聚焦在如何在这个限制内构建功能完整的体验。
Session 从三个层面讲解瘦身策略:编译期裁剪、资源优化和架构设计。编译期方面,Xcode 13 新增了 App Clip 大小报告,构建完成后会直接显示包体大小明细。资源方面,建议用 Asset Catalog 的 app-clip tag 来标记 App Clip 专用资源,避免把完整 app 的资源全部打包进去。
架构层面,Session 引入了一个重要观点:App Clip 不应该是完整 app 的精简版,而应该是一个独立的功能切片。这意味着你应该按使用场景设计 App Clip 的代码结构,而不是做减法。
值得深挖的点
动态库 vs 静态链接的体积权衡
App Clip 默认使用静态链接来消除动态库的加载开销,但静态链接会把所有用到的符号都编入二进制。如果你的 App Clip 只用了一个庞大 SDK 的 10% 功能,整个 SDK 都会被链接进来。Session 建议对第三方依赖做模块化拆分:只引入你需要的子模块,而不是整个 SDK。对于 Swift Package Manager 管理的依赖,可以用 .product(name:package:) 精确引用。
资源按需加载策略
图片和本地化文件是包体膨胀的主要来源。Session 展示了一个技巧:App Clip 只包含核心 UI 资源,其他内容通过按需下载(On-Demand Resources)在运行时获取。比如一个停车 App Clip,核心缴费界面打包在 10MB 内,但停车场地图数据在用户进入后通过 ODR 下载。这样既满足了大小限制,又不牺牲功能完整性。
代码片段
// 使用 Asset Catalog 的 app-clip tag 区分资源
// 在 Asset Catalog 中,给 App Clip 专用图片添加 "app-clip" tag
// 完整 app 专用图片不添加此 tag
// Xcode 会自动在 App Clip target 中排除无 tag 的资源
// 按需加载非核心资源
import OnDemandResources
class ResourceLoader {
let resourceRequest: NSBundleResourceRequest
init(tags: Set<String>) {
resourceRequest = NSBundleResourceRequest(tags: tags)
}
func loadMapData() async throws -> Data {
// 请求 ODR 资源
try await resourceRequest.beginAccessingResources()
defer { resourceRequest.endAccessingResources() }
guard let url = Bundle.main.url(
forResource: "parking_map",
withExtension: "json"
) else {
throw ResourceError.notFound
}
return try Data(contentsOf: url)
}
}
enum ResourceError: Error {
case notFound
}
// 检查 App Clip 包体大小(构建脚本)
// 在 Build Phase 中添加 Run Script:
// xcodebuild -scheme "AppClip" -destination 'generic/platform=iOS'
// archive -archivePath build.xcarchive
// OTHER_CODE_SIGN_FLAGS=--generate-entitlement-der
//
// 构建后在 Report Navigator 中查看 App Clip Size Report
最佳实践
在 CI 流水线中加入包体大小检查。如果 App Clip 的包体超过 8MB(留 2MB 余量),自动构建失败。可以用 xcrun --sdk iphoneos size 命令在构建后获取精确大小。
对于 SwiftUI 视图,避免在 App Clip 中使用大量的 SF Symbols 变体。每个 SF Symbol 的渲染模板都会占用空间,如果只用到标准变体,就不要引入 filled、circle 等变体。
还有什么值得关注
- Xcode 13 的 Organizer 中新增了 App Clip 的安装转化率指标,可以看到有多少用户从 App Clip 跳转到完整 app。
- Swift Concurrency 在 App Clip 中完全可用,async/await 不会增加额外的运行时体积。
- 如果你的 App Clip 使用 Core ML 模型,考虑把模型放在服务端通过 API 调用,一个 5MB 的 mlmodel 文件会直接吃掉一半的预算。