/tipkit
Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup
$ npx -y skills add dpearson2699/swift-ios-skills --skill tipkit --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/tipkit
Context preview
The summary Claude sees to decide when to auto-load this skill.
Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup
SKILL.md
tipkit.SKILL.mdname: tipkit
description: "Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation."
TipKit
Use TipKit for small, contextual feature-discovery moments: inline tips, popover tips, rule-gated education, and lightweight coach marks. Keep generic SwiftUI architecture, navigation, layout, and long first-run onboarding flows in their sibling skills unless TipKit presentation is the core issue.
Contents
- [Availability](#availability)
- [Configure TipKit](#configure-tipkit)
- [Design Good Tips](#design-good-tips)
- [Define Tips](#define-tips)
- [Present Tips](#present-tips)
- [Rules and Events](#rules-and-events)
- [Options and Invalidation](#options-and-invalidation)
- [Actions and Styles](#actions-and-styles)
- [Tip Groups](#tip-groups)
- [Testing](#testing)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
Availability
TipKit's core `Tip`, `TipView`, `popoverTip`, rules, events, options, and testing overrides are available on iOS 17+, iPadOS 17+, macOS 14+, tvOS 17+, watchOS 10+, and visionOS 1+.
Gate newer APIs explicitly:
| API | Availability | Use | | --- | --- | --- | | `TipGroup` | iOS 18+ | Group or sequence tips; apply the [Tip Groups](#tip-groups) decision. | | `.cloudKitContainer(...)` | iOS 18+ | Sync tip state, parameters, events, and display counts across devices. | | `MaxDisplayDuration` | iOS 18+ | Automatically invalidate after cumulative display time. | | `resetEligibility()` | iOS 26+ | Make a previously invalidated tip eligible again without resetting the datastore. |
Configure TipKit
Call `Tips.configure(_:)` once during app initialization, before any tip can display. Do not configure TipKit from a view's `onAppear` or `.task`.
import SwiftUI
import TipKit
@main
struct MyApp: App {
init() {
do {
try Tips.configure([
.datastoreLocation(.applicationDefault),
.displayFrequency(.daily)
])
} catch {
assertionFailure("TipKit configuration failed: \(error)")
}
}
var body: some Scene {
WindowGroup { ContentView() }
}
}Use `.datastoreLocation(.groupContainer(identifier:))` only when an app and extension or app-group members intentionally share tip state. Keep option settings consistent across app-group members because TipKit persists option state with the tip record.
CloudKit Sync
Use CloudKit sync only on iOS 18+ and later. Enable iCloud + CloudKit and Background Modes > Remote notifications, then pass a container:
try Tips.configure([
.cloudKitContainer(.named("iCloud.com.example.app.tips"))
])Prefer a dedicated container with a `.tips` suffix. `.automatic` uses the first entitled `.tips` container when present, then falls back to the primary container.
Design Good Tips
Tips are small, transient help. Use them for features people can understand and try in a few simple steps. If the flow needs a long explanation, multiple screens, or critical safety/error information, use a tutorial, alert, inline warning, or onboarding flow instead.
Follow HIG-aligned defaults:
- Keep titles short, direct, and action-oriented.
- Use one or two sentences; avoid promotional or unrelated copy.
- Place tips near the feature they explain.
- Prefer inline tips when hiding nearby UI would interrupt the task.
- Prefer popover tips when preserving the current layout matters and the tip can
point to a specific control.
- Use rules and display frequency so only the right audience sees each tip.
- Avoid repeating an icon in the tip when the popover already points to that icon.
Define Tips
`Tip` conforms to `Identifiable` and `Sendable`. Provide `title` at minimum; add `message`, `image`, `actions`, `rules`, `options`, and `id` only when they improve the feature-discovery moment.
import TipKit
struct FavoriteTip: Tip {
var title: Text { Text("Save to Favorites") }
var message: Text? { Text("Tap the heart to keep items for quick access.") }
var image: Image? { Image(systemName: "heart.fill") }
}By default, TipKit uses the tip type name as `id`. Override `id` for reusable tips whose persisted state should vary by content:
struct NewItemTip: Tip {
let itemID: Item.ID
var id: String { "NewItemTip-\(itemID)" }
var title: Text { Text("New Item Available") }
}Use stable, concrete identifiers. Do not derive IDs from transient copy or unstable ordering.
Present Tips
Use `TipView` for inline tips:
let favoriteTip = FavoriteTip()
VStack {
TipView(favoriteTip, arrowEdge: .bottom)
ItemListView()
}Use `.popoverTip` when the tip should point to a control:
Button {
toggleFavorite()
favoriteTip.invalidate(reason: .actionPerformed)
} label: {
Image(systemName: "heart")
}
.popoverTip(favoriteTip, arrowEdge: .top)Rules and Events
Rules are ANDed together. A tip becomes eligible only when every rule passes.
Use `@Parameter` for persisted app state:
struct FavoriteTip: Tip {
@Parameter static var hasSeenList = false
var title: Text { Text("Save to Favorites") }
var rules: [Rule] {
#Rule(Self.$hasSeenList) { $0 == true }
}
}Use `Tips.Event` for repeated user actions. TipKit queries the most recent 1000 donations by default, so keep event rules bounded and intentional.
struct ShortcutTip: Tip {
static let manualSaveEvent = Tips.Event(id: "manualSave")
var title: Text { Text("Save Faster") }
var rules: [Rule] {
#Rule(Self.manualSaveEvent) {
$0.donations.donatedWithiRead more
name: tipkit description: "Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation."
TipKit
Use TipKit for small, contextual feature-discovery moments: inline tips, popover tips, rule-gated education, and lightweight coach marks. Keep generic SwiftUI architecture, navigation, layout, and long first-run onboarding flows in their sibling skills unless TipKit presentation is the core issue.
Contents
- [Availability](#availability)
- [Configure TipKit](#configure-tipkit)
- [Design Good Tips](#design-good-tips)
- [Define Tips](#define-tips)
- [Present Tips](#present-tips)
- [Rules and Events](#rules-and-events)
- [Options and Invalidation](#options-and-invalidation)
- [Actions and Styles](#actions-and-styles)
- [Tip Groups](#tip-groups)
- [Testing](#testing)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
Availability
TipKit's core `Tip`, `TipView`, `popoverTip`, rules, events, options, and testing overrides are available on iOS 17+, iPadOS 17+, macOS 14+, tvOS 17+, watchOS 10+, and visionOS 1+.
Gate newer APIs explicitly:
| API | Availability | Use | | --- | --- | --- | | `TipGroup` | iOS 18+ | Group or sequence tips; apply the [Tip Groups](#tip-groups) decision. | | `.cloudKitContainer(...)` | iOS 18+ | Sync tip state, parameters, events, and display counts across devices. | | `MaxDisplayDuration` | iOS 18+ | Automatically invalidate after cumulative display time. | | `resetEligibility()` | iOS 26+ | Make a previously invalidated tip eligible again without resetting the datastore. |
Configure TipKit
Call `Tips.configure(_:)` once during app initialization, before any tip can display. Do not configure TipKit from a view's `onAppear` or `.task`.
import SwiftUI
import TipKit
@main
struct MyApp: App {
init() {
do {
try Tips.configure([
.datastoreLocation(.applicationDefault),
.displayFrequency(.daily)
])
} catch {
assertionFailure("TipKit configuration failed: \(error)")
}
}
var body: some Scene {
WindowGroup { ContentView() }
}
}Use `.datastoreLocation(.groupContainer(identifier:))` only when an app and extension or app-group members intentionally share tip state. Keep option settings consistent across app-group members because TipKit persists option state with the tip record.
CloudKit Sync
Use CloudKit sync only on iOS 18+ and later. Enable iCloud + CloudKit and Background Modes > Remote notifications, then pass a container:
try Tips.configure([
.cloudKitContainer(.named("iCloud.com.example.app.tips"))
])Prefer a dedicated container with a `.tips` suffix. `.automatic` uses the first entitled `.tips` container when present, then falls back to the primary container.
Design Good Tips
Tips are small, transient help. Use them for features people can understand and try in a few simple steps. If the flow needs a long explanation, multiple screens, or critical safety/error information, use a tutorial, alert, inline warning, or onboarding flow instead.
Follow HIG-aligned defaults:
- Keep titles short, direct, and action-oriented.
- Use one or two sentences; avoid promotional or unrelated copy.
- Place tips near the feature they explain.
- Prefer inline tips when hiding nearby UI would interrupt the task.
- Prefer popover tips when preserving the current layout matters and the tip can
point to a specific control.
- Use rules and display frequency so only the right audience sees each tip.
- Avoid repeating an icon in the tip when the popover already points to that icon.
Define Tips
`Tip` conforms to `Identifiable` and `Sendable`. Provide `title` at minimum; add `message`, `image`, `actions`, `rules`, `options`, and `id` only when they improve the feature-discovery moment.
import TipKit
struct FavoriteTip: Tip {
var title: Text { Text("Save to Favorites") }
var message: Text? { Text("Tap the heart to keep items for quick access.") }
var image: Image? { Image(systemName: "heart.fill") }
}By default, TipKit uses the tip type name as `id`. Override `id` for reusable tips whose persisted state should vary by content:
struct NewItemTip: Tip {
let itemID: Item.ID
var id: String { "NewItemTip-\(itemID)" }
var title: Text { Text("New Item Available") }
}Use stable, concrete identifiers. Do not derive IDs from transient copy or unstable ordering.
Present Tips
Use `TipView` for inline tips:
let favoriteTip = FavoriteTip()
VStack {
TipView(favoriteTip, arrowEdge: .bottom)
ItemListView()
}Use `.popoverTip` when the tip should point to a control:
Button {
toggleFavorite()
favoriteTip.invalidate(reason: .actionPerformed)
} label: {
Image(systemName: "heart")
}
.popoverTip(favoriteTip, arrowEdge: .top)Rules and Events
Rules are ANDed together. A tip becomes eligible only when every rule passes.
Use `@Parameter` for persisted app state:
struct FavoriteTip: Tip {
@Parameter static var hasSeenList = false
var title: Text { Text("Save to Favorites") }
var rules: [Rule] {
#Rule(Self.$hasSeenList) { $0 == true }
}
}Use `Tips.Event` for repeated user actions. TipKit queries the most recent 1000 donations by default, so keep event rules bounded and intentional.
struct ShortcutTip: Tip {
static let manualSaveEvent = Tips.Event(id: "manualSave")
var title: Text { Text("Save Faster") }
var rules: [Rule] {
#Rule(Self.manualSaveEvent) {
$0.donations.donatedWithi86 agent skills optimized for iOS 26+ development with Swift 6.3 and modern Apple frameworks.
Repo: dpearson2699/swift-ios-skills
Other skills on swift-ios-skills.
- /accessorysetupkit
Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery descriptors for BLE or Wi-Fi devices, handling accessory session events, migrating from CoreBluetooth permission-based
Open skill - /activitykit
Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island — delivery tracking, sports scores, ride-sharing status, workout timers, media
Open skill - /adattributionkit
Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing
Open skill - /alarmkit
Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, system Stop and AlarmButton secondary actions, authorization,
Open skill - /app-clips
Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences, size/capability constraints, NSUserActivity routing, SKOverlay promotion, App Group/keychain handoff, ephemeral notifications,
Open skill - /app-intents
Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and
Open skill

