skills/generators/tipkit-generator/SKILL.md
Generate TipKit infrastructure with inline/popover tips, rules, display frequency, and testing utilities. Use when adding contextual tips or feature discovery to an iOS/macOS app.
npx skillsauth add rshankras/claude-code-apple-skills tipkit-generatorInstall 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 a complete TipKit setup for contextual tips and feature discovery, including tip definitions, rules, display frequency, inline and popover presentation, and testing utilities.
Use this skill when the user:
Search for existing TipKit usage:
Glob: **/*Tip*.swift
Grep: "import TipKit" or "Tips.configure"
If found, ask user:
Ask user via AskUserQuestion:
What features need tips?
Tip presentation style? (per tip or general preference)
Rule types needed?
Display frequency?
Tip ordering?
Read the templates file for code patterns:
Read("skills/generators/tipkit-generator/templates.md")
Generate these files based on configuration:
Tips/ directory with one file per tip (e.g., SearchTip.swift, FilterTip.swift)Tips/TipEvents.swift - Centralized event definitionsTips/TipsConfiguration.swift - Tips.configure() setup and testing utilitiesCheck project structure:
Sources/ exists -> Sources/Tips/App/ exists -> App/Tips/Tips/Tips.configure() call in App entry pointTipView or .popoverTip() at the appropriate view locationsAfter generation, provide:
Sources/Tips/
├── SearchTip.swift # Tip with rules and options
├── FilterTip.swift # Another tip definition
├── TipEvents.swift # Centralized event definitions
└── TipsConfiguration.swift # Tips.configure() + testing helpers
App Entry Point (Required):
import TipKit
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.task {
try? Tips.configure([
.displayFrequency(.daily),
.datastoreLocation(.applicationDefault)
])
}
}
}
}
Inline Tip:
import TipKit
struct SearchView: View {
let searchTip = SearchTip()
var body: some View {
VStack {
TipView(searchTip)
SearchBar()
}
}
}
Popover Tip:
import TipKit
struct ToolbarView: View {
let filterTip = FilterTip()
var body: some View {
Button("Filter", systemImage: "line.3.horizontal.decrease.circle") {
// action
}
.popoverTip(filterTip)
}
}
Event Donation (at action site):
Button("Search") {
performSearch()
SearchTip.searchPerformed.donate()
}
Tip Invalidation (when tip is no longer relevant):
func onFeatureUsed() {
// User discovered the feature, invalidate the tip
searchTip.invalidate(reason: .actionPerformed)
}
Reset DataStore between runs:
// Add to a debug menu or call in preview
try? Tips.resetDatastore()
Show all tips for testing:
// Ignores rules and frequency -- shows everything
Tips.showAllTipsForTesting()
Show specific tips for testing:
Tips.showTipsForTesting([SearchTip.self])
Test scenarios:
Forgetting Tips.configure() -- Tips will never appear if you do not call Tips.configure() before any tip is displayed. This must happen early, typically in the App body or .task.
Rules not evaluating -- Parameter-based rules require you to set the parameter value explicitly. If you define @Parameter static var hasSeenFeature = false but never set it to true, the rule never passes.
DataStore conflicts in tests -- If you run unit tests and the app simultaneously, they may share the same DataStore. Use .datastoreLocation(.url(...)) to isolate them.
Display frequency blocking tips -- If you set .displayFrequency(.daily) and a tip was already shown today, no new tips will appear until tomorrow. Use .immediate during development.
Tips not dismissing after invalidation -- You must hold a reference to the tip instance and call .invalidate(reason:) on that instance. Creating a new instance and invalidating it does nothing to the displayed tip.
TipGroup ordering ignored -- Tips in a TipGroup only show in order if their rules are all satisfied. If Tip B's rules pass but Tip A's do not, neither will show (Tip A blocks Tip B).
Good tips are actionable, instructional, and easy to remember:
Never use tips for:
$0.donations.donatedWithin(.days(5)).count >= 3 — "3 times in the past 5 days", not just a lifetime countIgnoresDisplayFrequency(true) tip option exempts an urgent tip from the global .displayFrequency cadenceMaxDisplayCount(n) tip option auto-stops a tip after N impressions, even if it is never dismissed or invalidated.actionPerformed invalidation reason when the user completes the action the tip describesTipGroup when tips should appear in a logical sequenceTips.resetDatastore()Tips.configure() in the App entry point.immediate display frequency in production (overwhelming users)TipView inside a ScrollView without considering layout impactdevelopment
US web checkout via the StoreKit External Purchase Link entitlement — currently 0% Apple commission (litigation ongoing), how to ship it safely, and how to architect for a commission flip so a future ruling is a config change, not a rewrite. Use when adding external purchase links, weighing web checkout vs IAP, or planning US-storefront pricing strategy.
tools
Revenue beyond the single-app price tag — own-app bundles, Family Sharing as a conversion lever, cross-developer bundles & suites, and institutional licensing via Group Purchases / Apple School & Business Manager. Use when a developer has multiple apps, a subscription worth sharing, complementary indie partners, or school/clinic/business buyers.
testing
Run a structured accessibility audit on an iOS/macOS app — automated XCUITest audits, Accessibility Inspector, manual VoiceOver/Dynamic Type passes, and App Store Accessibility Nutrition Label evaluation. Use before release, when preparing Nutrition Label declarations, or for EU Accessibility Act compliance.
tools
Stage-by-stage audit of an app's App Store growth machinery against a 54-item P0–P9 playbook — every item scored from an App Store Connect MCP call, a codebase check, or an explicit question to the user, then routed to the skill or command that fixes it. Read-only on App Store Connect. Use for a growth audit or scorecard, a pre-launch growth plan, a quarterly re-audit, or "which growth levers am I missing."