skills/generators/app-clip/SKILL.md
Generates App Clip targets with invocation URL handling, lightweight experiences, and full app upgrade prompts. Use when user wants NFC/QR/Safari banner invocation, instant app experiences, or App Clip Card setup.
npx skillsauth add rshankras/claude-code-apple-skills app-clipInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate production App Clip infrastructure — a lightweight version of your app invoked from NFC tags, QR codes, Safari banners, or Messages. Includes App Clip target setup, invocation URL handling, experience routing, location confirmation, and full app upgrade flow.
Use this skill when the user:
Search for existing App Clip targets:
Glob: **/*AppClip*/*.swift, **/*Clip*/*.swift
Grep: "NSUserActivityTypeBrowsingWeb" or "AppClipExperience" or "SKOverlay"
If existing App Clip target found:
Identify where the main app target lives and where to place the App Clip target alongside it.
Ask user via AskUserQuestion:
Invocation method?
Primary experience?
Include location confirmation?
Include full app upgrade prompt?
Read templates.md for production Swift code.
Read patterns.md for constraints, testing, and best practices.
Generate these files:
AppClipApp.swift — @main App struct handling invocation via .onContinueUserActivityInvocationHandler.swift — Parses invocation URL, extracts parameters, validates against registered experiencesAppClipExperience.swift — Protocol and concrete experience implementationsLocationConfirmationView.swift — CLLocationManager-based location verification for physical invocationsFullAppUpgradeView.swift — SKOverlay-based banner prompting full app downloadSharedDataManager.swift — App Group data sharing between App Clip and full appCheck project structure:
Sources/ exists -> Sources/AppClip/AppClip/ at the same levelAppClip/After generation, provide:
AppClip/
├── AppClipApp.swift # @main entry point with invocation handling
├── InvocationHandler.swift # URL parsing and parameter extraction
├── AppClipExperience.swift # Experience protocol and implementations
├── LocationConfirmationView.swift # Location verification (optional)
├── FullAppUpgradeView.swift # SKOverlay upgrade prompt (optional)
└── SharedDataManager.swift # App Group data sharing (optional)
Add App Clip Target:
{main-app-bundle-id}.ClipConfigure Associated Domains:
appclips:{your-domain.com} to both main app and App Clip entitlementsSet Up App Group:
group.{your-bundle-id} to both targets for shared dataApple-App-Site-Association (AASA) file:
https://{your-domain.com}/.well-known/apple-app-site-associationHandle invocation in the App Clip:
@main
struct MyAppClip: App {
@State private var handler = InvocationHandler()
var body: some Scene {
WindowGroup {
ContentView(experience: handler.currentExperience)
.onContinueUserActivity(
NSUserActivityTypeBrowsingWeb
) { activity in
handler.handle(activity)
}
}
}
}
Route to the correct experience:
struct ContentView: View {
let experience: (any AppClipExperience)?
var body: some View {
if let experience {
AnyView(experience.makeView())
} else {
DefaultExperienceView()
}
}
}
Share data with the full app:
// In App Clip — save order before user upgrades
SharedDataManager.shared.save(order, forKey: "pendingOrder")
// In Full App — restore after install
if let order: Order = SharedDataManager.shared.load(forKey: "pendingOrder") {
showOrder(order)
}
Prompt full app download:
FullAppUpgradeView(
appStoreID: "123456789",
benefits: [
"Order history and favorites",
"Loyalty rewards program",
"Push notification for order updates"
]
)
@Test
func invocationHandlerParsesProductURL() {
let handler = InvocationHandler()
let url = URL(string: "https://example.com/clip/product/abc123")!
let experience = handler.parseURL(url)
#expect(experience != nil)
#expect(experience?.experienceType == .previewContent)
#expect(experience?.parameters["productID"] == "abc123")
}
@Test
func invocationHandlerRejectsInvalidURL() {
let handler = InvocationHandler()
let url = URL(string: "https://other-domain.com/something")!
let experience = handler.parseURL(url)
#expect(experience == nil)
}
@Test
func sharedDataManagerRoundTrips() {
let manager = SharedDataManager(suiteName: "group.test")
let order = Order(id: "order-1", items: ["Latte", "Muffin"])
manager.save(order, forKey: "testOrder")
let loaded: Order? = manager.load(forKey: "testOrder")
#expect(loaded?.id == "order-1")
#expect(loaded?.items.count == 2)
}
Every App Clip starts from a URL. Parse it to determine what experience to show:
// URL: https://example.com/clip/order?location=store-42
// -> Route to OrderExperience with locationID = "store-42"
Users expect instant value. Show the relevant experience within 1-2 seconds, no sign-in required.
After the user completes the primary task, show an SKOverlay banner with clear benefits of the full app.
appclips: associated domain configured, and the AASA file must be hosted on the domain.xcodebuild -exportArchive or the App Thinning Size Report.generators/deep-linking — Universal link and deep link handlinggenerators/onboarding-generator — Onboarding flow for full app upgradedevelopment
Build, install, and launch an iOS app on a physical iPhone or iPad entirely from the command line (no Xcode GUI), using xcodebuild + devicectl. Use when the user wants to run, test, or screenshot their app on a real device without opening Xcode.
development
Comprehensive iOS development guidance including Swift best practices, SwiftUI patterns, UI/UX review against HIG, and app planning. Use for iOS code review, best practices, accessibility audits, or planning new iOS apps.
development
Build, install, launch, and screenshot an iOS app in the Simulator to verify a change visually. Use when the user wants to run the app, see a change live, screenshot the running app, or confirm a UI fix actually works (not just that it compiles).
development
Audits skills in this repo for consistency, API drift, and structural gaps. Produces a prioritized report grouped by severity (Critical/High/Medium/Low). Use when asked to "audit skills", "check the skill repo for drift", or when planning bulk skill cleanup. Read-only — does not apply fixes.