app-clips by dpearson2699/swift-ios-skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill app-clips适用于即时体验或演示的 iOS 应用轻量级、即时可用版本。除非另有说明,目标平台为 iOS 26+ / Swift 6.2。
App Clip 是您完整应用所在的同一个 Xcode 项目中的一个独立目标:
com.apple.developer.on-demand-install-capable 权限和一个链接回完整应用的 Parent Application Identifiers 权限。com.example.MyApp.Clip。使用 Swift 包或共享源文件。将文件添加到两个目标,或使用 活动编译条件:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
APPCLIP// 在 App Clip 目标的构建设置 → 活动编译条件: APPCLIP
#if !APPCLIP
// 仅限完整应用的代码(例如,后台任务、App Intents)
#else
// App Clip 特定代码
#endif
对于共享模块,优先使用本地 Swift 包 — 将该包添加为两个目标的依赖项。
创建一个包含在两个目标中的共享资产目录,以避免重复的图像和颜色资源。
App Clip 在启动时会收到一个类型为 NSUserActivityTypeBrowsingWeb 的 NSUserActivity。使用 onContinueUserActivity 处理它:
@main
struct DonutShopClip: App {
var body: some Scene {
WindowGroup {
ContentView()
.onContinueUserActivity(
NSUserActivityTypeBrowsingWeb
) { activity in
handleInvocation(activity)
}
}
}
private func handleInvocation(_ activity: NSUserActivity) {
guard let url = activity.webpageURL,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
else { return }
// 提取路径/查询参数以确定上下文
let locationID = components.queryItems?
.first(where: { $0.name == "location" })?.value
// 为此位置更新 UI
}
}
对于基于 UIKit 场景的应用,为冷启动实现 scene(_:willConnectTo:options:),为热启动实现 scene(_:continue:)。
关键规则: 完整应用必须以相同的方式处理所有调用 URL — 当用户安装完整应用时,它将替换 App Clip 并接收所有未来的调用。
上传包含 App Clip 的构建版本后,在 App Store Connect 中配置体验。
https://appclip.apple.com/id?=<bundle_id>&key=value对于自定义 URL(非 Apple 生成的默认链接),将条目添加到关联域名权限并托管一个 AASA 文件:
appclips:example.com
App Clip 二进制文件必须保持在严格的解压后大小限制内(通过 App Thinning 大小报告测量):
| iOS 版本 | 最大解压大小 |
|---|---|
| iOS 15 及更早版本 | 10 MB |
| iOS 16 | 15 MB |
| iOS 17+(仅限数字调用) | 100 MB |
| iOS 17+(通过演示链接,所有调用) | 100 MB |
iOS 17+ 上非演示链接的 100 MB 限制要求:仅限数字调用,不支持物理调用(不支持 App Clip 码 / NFC / 二维码),并且 App Clip 必须不支持 iOS 16 或更早版本。
测量大小: 归档应用 → 分发 → 使用 App Thinning 导出为 Ad Hoc/开发版本 → 检查 App Thinning Size Report.txt。
如果需要,可以使用后台资源在启动后下载额外内容(例如,游戏关卡)。App Clip 下载不能使用 isEssential。
| 方法 | 要求 |
|---|---|
| App Clip 码 | 高级体验或演示链接;NFC 集成或仅扫描 |
| NFC 标签 | 在 NDEF 负载中编码调用 URL |
| 二维码 | 编码调用 URL;适用于默认或高级体验 |
| Safari 智能横幅 | 将 App Clip 与网站关联;添加 <meta> 标签 |
| 地图 | 具有地点关联的高级体验 |
| 信息 | 以文本形式分享调用 URL;演示链接的预览有限 |
| Siri 建议 | 基于位置;需要高级体验以获取位置建议 |
| 其他应用 | iOS 17+;使用 Link Presentation 或 UIApplication.open(_:) |
将此元标签添加到您的网站以显示 App Clip 横幅:
<meta name="apple-itunes-app"
content="app-id=YOUR_APP_ID, app-clip-bundle-id=com.example.MyApp.Clip,
app-clip-display=card">
当用户安装完整应用时,它会替换 App Clip。使用共享的 App Group 容器来迁移数据:
// 在两个目标中:添加具有相同组 ID 的 App Groups 能力
// App Clip — 写入数据
func saveOrderHistory(_ orders: [Order]) throws {
guard let containerURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp.shared"
) else { return }
let data = try JSONEncoder().encode(orders)
let fileURL = containerURL.appendingPathComponent("orders.json")
try data.write(to: fileURL)
}
// 完整应用 — 读取迁移的数据
func loadMigratedOrders() throws -> [Order] {
guard let containerURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp.shared"
) else { return [] }
let fileURL = containerURL.appendingPathComponent("orders.json")
guard FileManager.default.fileExists(atPath: fileURL.path) else { return [] }
let data = try Data(contentsOf: fileURL)
return try JSONDecoder().decode([Order].self, from: data)
}
// 写入(App Clip)
let shared = UserDefaults(suiteName: "group.com.example.myapp.shared")
shared?.set(userToken, forKey: "authToken")
// 读取(完整应用)
let shared = UserDefaults(suiteName: "group.com.example.myapp.shared")
let token = shared?.string(forKey: "authToken")
从 iOS 15.4 开始,完整应用可以通过 parent-application-identifiers 和 associated-appclip-app-identifiers 权限访问相应的 App Clip 钥匙串项。使用不同的 kSecAttrLabel 值来区分 App Clip 和完整应用的条目。
将 ASAuthorizationAppleIDCredential.user 存储在共享容器中,以便完整应用可以在不重新提示登录的情况下静默验证。
在 App Clip 内显示一个推荐完整应用的覆盖层:
struct OrderCompleteView: View {
@State private var showOverlay = false
var body: some View {
VStack {
Text("订单已下达!")
Button("获取完整应用") { showOverlay = true }
}
.appStoreOverlay(isPresented: $showOverlay) {
SKOverlay.AppClipConfiguration(position: .bottom)
}
}
}
func displayOverlay() {
guard let scene = view.window?.windowScene else { return }
let config = SKOverlay.AppClipConfiguration(position: .bottom)
let overlay = SKOverlay(configuration: config)
overlay.delegate = self
overlay.present(in: scene)
}
SKOverlay.AppClipConfiguration 会自动解析到父应用。适用于 iOS 14.0+。
切勿阻止用户的任务以强制安装 — 在任务完成后显示覆盖层。
使用 APActivationPayload 来验证用户的物理位置,而无需请求完整的位置访问权限:
import AppClip
import CoreLocation
func verifyLocation(from activity: NSUserActivity) {
guard let payload = activity.appClipActivationPayload,
let url = activity.webpageURL
else { return }
// 构建预期区域(半径最大 500 米)
let center = CLLocationCoordinate2D(latitude: 37.334722, longitude: -122.008889)
let region = CLCircularRegion(center: center, radius: 100, identifier: "store-42")
payload.confirmAcquired(in: region) { inRegion, error in
if let error = error as? APActivationPayloadError {
switch error.code {
case .doesNotMatch:
// URL 与已注册的 App Clip URL 不匹配
break
case .disallowed:
// 用户拒绝了位置,或者调用不是通过 NFC/视觉码
break
@unknown default:
break
}
return
}
if inRegion {
// 已确认 — 用户在预期位置
} else {
// 用户不在预期位置(例如,NFC 标签被移动)
}
}
}
在 Info.plist 中启用位置确认:
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestLocationConfirmation</key>
<true/>
</dict>
这是轻量级的 — 系统会验证位置,而无需授予您的 App Clip 持续访问权限。App Clip 卡片会显示一条说明,表明该片段可以验证位置。适用于 iOS 14.0+。
Info.plist 请求临时通知权限(最长 8 小时);将 NSAppClipRequestEphemeralUserNotification 设置为 truerequestWhenInUseAuthorization();每天凌晨 4:00 重置SwiftUI, UIKit, Core Location(使用时), 通过 Apple 登录, Apple Pay, CloudKit(公共数据库只读,iOS 16+), Background Assets, StoreKit (SKOverlay), Keychain, App Groups, 推送通知(临时), Live Activities (iOS 16.1+)
App Intents, Background Tasks, CallKit, Contacts, CoreMotion, EventKit, HealthKit, HomeKit, MediaPlayer, Messages, NearbyInteraction, PhotoKit, SensorKit, Speech, SKAdNetwork, App Tracking Transparency
UIDevice.name 和 identifierForVendor 返回空字符串// ❌ 不要:包含大型框架或捆绑的资产
// 导入像 RealityKit 这样的重量级框架或大型 ML 模型
// 会使 App Clip 大小远超 10–15 MB。
// ✅ 应该:使用资产目录优化,排除未使用的架构,
// 剥离调试符号,并将共享代码拆分为精简的 Swift 包。
// 每次更改后使用 App Thinning 大小报告进行测量。
// ❌ 不要:仅通过直接 Xcode 启动测试 App Clip
// 这会跳过调用 URL 处理并遗漏错误。
// ✅ 应该:在方案中使用 _XCAppClipURL 环境变量,
// 或在 设置 → 开发者 → 本地体验 中注册本地体验
// 以使用真实的调用 URL 和 App Clip 卡片进行测试。
// ❌ 不要:假设只有 App Clip 会收到调用
// 当用户安装完整应用时,所有调用都会发送给它。
// ✅ 应该:在两个目标之间共享调用处理代码。
// 完整应用必须处理 App Clip 支持的每一个调用 URL。
#if !APPCLIP
// 完整应用可以针对相同的 URL 额外显示更丰富的功能
#endif
// ❌ 不要:将重要数据存储在 App Clip 的沙盒容器中
let fileURL = documentsDirectory.appendingPathComponent("userData.json")
// 当系统移除 App Clip 时,此数据会被删除。
// ✅ 应该:写入共享的 App Group 容器或同步到服务器
guard let shared = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.shared"
) else { return }
let fileURL = shared.appendingPathComponent("userData.json")
// ❌ 不要:在 App Store Connect 中配置 App Clip 体验
// 而不设置关联域名和 AASA 文件。
// 来自您网站和高级体验的调用将失败。
// ✅ 应该:添加关联域名权限,包含:
// appclips:example.com
// 并在您的服务器上托管 /.well-known/apple-app-site-association:
// {
// "appclips": {
// "apps": ["TEAMID.com.example.MyApp.Clip"]
// }
// }
Parent Application Identifiers 权限设置正确APPCLIP)onContinueUserActivity(NSUserActivityTypeBrowsingWeb) 处理调用 URLappclips:yourdomain.com(如果使用自定义 URL)/.well-known/apple-app-site-association 托管 AASA 文件(如果使用自定义 URL)SKOverlay / appStoreOverlay 在任务完成后显示,绝不阻塞NSAppClipRequestLocationConfirmation_XCAppClipURL 环境变量进行测试每周安装次数
325
代码仓库
GitHub 星标
269
首次出现
2026年3月8日
安全审计
安装于
codex322
opencode319
github-copilot319
amp319
cline319
kimi-cli319
Lightweight, instantly-available versions of your iOS app for in-the-moment experiences or demos. Targets iOS 26+ / Swift 6.2 unless noted.
An App Clip is a separate target in the same Xcode project as your full app:
com.apple.developer.on-demand-install-capable entitlement and a Parent Application Identifiers entitlement linking back to the full app.com.example.MyApp.Clip.Use Swift packages or shared source files. Add files to both targets, or use the APPCLIP active compilation condition:
// In App Clip target Build Settings → Active Compilation Conditions: APPCLIP
#if !APPCLIP
// Full-app-only code (e.g., background tasks, App Intents)
#else
// App Clip specific code
#endif
Prefer local Swift packages for shared modules — add the package as a dependency of both targets.
Create a shared asset catalog included in both targets to avoid duplicating images and colors.
App Clips receive an NSUserActivity of type NSUserActivityTypeBrowsingWeb on launch. Handle it with onContinueUserActivity:
@main
struct DonutShopClip: App {
var body: some Scene {
WindowGroup {
ContentView()
.onContinueUserActivity(
NSUserActivityTypeBrowsingWeb
) { activity in
handleInvocation(activity)
}
}
}
private func handleInvocation(_ activity: NSUserActivity) {
guard let url = activity.webpageURL,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)
else { return }
// Extract path/query to determine context
let locationID = components.queryItems?
.first(where: { $0.name == "location" })?.value
// Update UI for this location
}
}
For UIKit scene-based apps, implement scene(_:willConnectTo:options:) for cold launch and scene(_:continue:) for warm launch.
Key rule: The full app must handle all invocation URLs identically — when a user installs the full app, it replaces the App Clip and receives all future invocations.
Configure experiences in App Store Connect after uploading a build containing the App Clip.
https://appclip.apple.com/id?=<bundle_id>&key=valueFor custom URLs (not the default Apple-generated link), add entries to the Associated Domains entitlement and host an AASA file:
appclips:example.com
App Clip binaries must stay within strict uncompressed size limits (measured via App Thinning Size Report):
| iOS Version | Maximum Uncompressed Size |
|---|---|
| iOS 15 and earlier | 10 MB |
| iOS 16 | 15 MB |
| iOS 17+ (digital invocations only) | 100 MB |
| iOS 17+ (via demo link, all invocations) | 100 MB |
The 100 MB limit on iOS 17+ for non-demo links requires: digital-only invocations, no physical invocation support (no App Clip Codes / NFC / QR), and the App Clip must not support iOS 16 or earlier.
Measure size: Archive the app → Distribute → Export as Ad Hoc/Development with App Thinning → check App Thinning Size Report.txt.
Use Background Assets to download additional content post-launch (e.g., game levels) if needed. App Clip downloads cannot use isEssential.
| Method | Requirements |
|---|---|
| App Clip Codes | Advanced experience or demo link; NFC-integrated or scan-only |
| NFC tags | Encode invocation URL in NDEF payload |
| QR codes | Encode invocation URL; works with default or advanced experience |
| Safari Smart Banners | Associate App Clip with website; add <meta> tag |
| Maps | Advanced experience with place association |
| Messages | Share invocation URL as text; limited preview with demo links |
| Siri Suggestions | Location-based; requires advanced experience for location suggestions |
| Other apps | iOS 17+; use Link Presentation or UIApplication.open(_:) |
Add this meta tag to your website to show the App Clip banner:
<meta name="apple-itunes-app"
content="app-id=YOUR_APP_ID, app-clip-bundle-id=com.example.MyApp.Clip,
app-clip-display=card">
When a user installs the full app, it replaces the App Clip. Use a shared App Group container to migrate data:
// In both targets: add App Groups capability with the same group ID
// App Clip — write data
func saveOrderHistory(_ orders: [Order]) throws {
guard let containerURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp.shared"
) else { return }
let data = try JSONEncoder().encode(orders)
let fileURL = containerURL.appendingPathComponent("orders.json")
try data.write(to: fileURL)
}
// Full app — read migrated data
func loadMigratedOrders() throws -> [Order] {
guard let containerURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.myapp.shared"
) else { return [] }
let fileURL = containerURL.appendingPathComponent("orders.json")
guard FileManager.default.fileExists(atPath: fileURL.path) else { return [] }
let data = try Data(contentsOf: fileURL)
return try JSONDecoder().decode([Order].self, from: data)
}
// Write (App Clip)
let shared = UserDefaults(suiteName: "group.com.example.myapp.shared")
shared?.set(userToken, forKey: "authToken")
// Read (Full app)
let shared = UserDefaults(suiteName: "group.com.example.myapp.shared")
let token = shared?.string(forKey: "authToken")
Starting iOS 15.4, App Clip keychain items are accessible to the corresponding full app via the parent-application-identifiers and associated-appclip-app-identifiers entitlements. Use distinct kSecAttrLabel values to distinguish App Clip vs. full app entries.
Store the ASAuthorizationAppleIDCredential.user in the shared container so the full app can silently verify without re-prompting login.
Display an overlay recommending the full app from within the App Clip:
struct OrderCompleteView: View {
@State private var showOverlay = false
var body: some View {
VStack {
Text("Order placed!")
Button("Get the full app") { showOverlay = true }
}
.appStoreOverlay(isPresented: $showOverlay) {
SKOverlay.AppClipConfiguration(position: .bottom)
}
}
}
func displayOverlay() {
guard let scene = view.window?.windowScene else { return }
let config = SKOverlay.AppClipConfiguration(position: .bottom)
let overlay = SKOverlay(configuration: config)
overlay.delegate = self
overlay.present(in: scene)
}
SKOverlay.AppClipConfiguration automatically resolves to the parent app. Available iOS 14.0+.
Never block the user's task to force installation — show the overlay after task completion.
Use APActivationPayload to verify a user's physical location without requesting full location access:
import AppClip
import CoreLocation
func verifyLocation(from activity: NSUserActivity) {
guard let payload = activity.appClipActivationPayload,
let url = activity.webpageURL
else { return }
// Build the expected region (up to 500m radius)
let center = CLLocationCoordinate2D(latitude: 37.334722, longitude: -122.008889)
let region = CLCircularRegion(center: center, radius: 100, identifier: "store-42")
payload.confirmAcquired(in: region) { inRegion, error in
if let error = error as? APActivationPayloadError {
switch error.code {
case .doesNotMatch:
// URL doesn't match registered App Clip URL
break
case .disallowed:
// User denied location, or invocation wasn't NFC/visual code
break
@unknown default:
break
}
return
}
if inRegion {
// Confirmed — user is at the expected location
} else {
// User is not at expected location (e.g., NFC tag was moved)
}
}
}
Enable location confirmation in Info.plist:
<key>NSAppClip</key>
<dict>
<key>NSAppClipRequestLocationConfirmation</key>
<true/>
</dict>
This is lightweight — the system verifies location without granting your App Clip continuous access. The App Clip card shows a note that the clip can verify location. Available iOS 14.0+.
Info.plist; set NSAppClipRequestEphemeralUserNotification to truerequestWhenInUseAuthorization() only; resets daily at 4:00 AMSwiftUI, UIKit, Core Location (when-in-use), Sign in with Apple, Apple Pay, CloudKit (public database read-only, iOS 16+), Background Assets, StoreKit (SKOverlay), Keychain, App Groups, Push Notifications (ephemeral), Live Activities (iOS 16.1+)
App Intents, Background Tasks, CallKit, Contacts, CoreMotion, EventKit, HealthKit, HomeKit, MediaPlayer, Messages, NearbyInteraction, PhotoKit, SensorKit, Speech, SKAdNetwork, App Tracking Transparency
UIDevice.name and identifierForVendor return empty strings// ❌ DON'T: Include large frameworks or bundled assets
// Importing heavyweight frameworks like RealityKit or large ML models
// pushes the App Clip well over 10–15 MB.
// ✅ DO: Use Asset Catalog thinning, exclude unused architectures,
// strip debug symbols, and split shared code into lean Swift packages.
// Measure with App Thinning Size Report after every change.
// ❌ DON'T: Only test App Clip with a direct Xcode launch
// This skips invocation URL handling and misses bugs.
// ✅ DO: Use the _XCAppClipURL environment variable in the scheme,
// or register a Local Experience in Settings → Developer → Local Experiences
// to test with realistic invocation URLs and App Clip cards.
// ❌ DON'T: Assume only the App Clip receives invocations
// When the user installs the full app, ALL invocations go to it.
// ✅ DO: Share invocation-handling code between both targets.
// The full app must handle every invocation URL the App Clip supports.
#if !APPCLIP
// Full app can additionally show richer features for the same URL
#endif
// ❌ DON'T: Store important data in the App Clip's sandboxed container
let fileURL = documentsDirectory.appendingPathComponent("userData.json")
// This data is DELETED when the system removes the App Clip.
// ✅ DO: Write to the shared App Group container or sync to a server
guard let shared = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.shared"
) else { return }
let fileURL = shared.appendingPathComponent("userData.json")
// ❌ DON'T: Configure App Clip experiences in App Store Connect
// without setting up associated domains and the AASA file.
// Invocations from your website and advanced experiences will fail.
// ✅ DO: Add the Associated Domains entitlement with:
// appclips:example.com
// AND host /.well-known/apple-app-site-association on your server:
// {
// "appclips": {
// "apps": ["TEAMID.com.example.MyApp.Clip"]
// }
// }
Parent Application Identifiers entitlement is set correctlyAPPCLIP)onContinueUserActivity(NSUserActivityTypeBrowsingWeb) handles invocation URLsappclips:yourdomain.com (if using custom URLs)/.well-known/apple-app-site-association (if using custom URLs)SKOverlay / appStoreOverlay shown after task completion, never blockingWeekly Installs
325
Repository
GitHub Stars
269
First Seen
Mar 8, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
codex322
opencode319
github-copilot319
amp319
cline319
kimi-cli319
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
竞争对手研究指南:SEO、内容、反向链接与定价分析工具
231 周安装
Azure 工作负载自动升级评估工具 - 支持 Functions、App Service 计划与 SKU 迁移
231 周安装
Kaizen持续改进方法论:软件开发中的渐进式优化与防错设计实践指南
231 周安装
软件UI/UX设计指南:以用户为中心的设计原则、WCAG可访问性与平台规范
231 周安装
Apify 网络爬虫和自动化平台 - 无需编码抓取亚马逊、谷歌、领英等网站数据
231 周安装
llama.cpp 中文指南:纯 C/C++ LLM 推理,CPU/非 NVIDIA 硬件优化部署
231 周安装
NSAppClipRequestLocationConfirmation set in Info.plist if using location verification_XCAppClipURL env var