Skip to main content

tipkit-generator

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.

Source facts

Repository
rshankras/claude-code-apple-skills
Last source activity
July 16, 2026 at 12:36
Detected SKILL.md language
English
Stars
770
Forks
74

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
tipkit-generator
description
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.
allowed-tools
["Read","Write","Edit","Glob","Grep","Bash","AskUserQuestion"]
last_verified
2026-07-16T00:00:00.000Z
review_by
2027-06-22T00:00:00.000Z
os_version
iOS 27 / macOS 27
# TipKit Generator Generate a complete TipKit setup for contextual tips and feature discovery, including tip definitions, rules, display frequency, inline and popover presentation, and testing utilities. ## When This Skill Activates Use this skill when the user: - Asks to "add tips" or "add TipKit" - Mentions "contextual tips" or "feature discovery" - Wants "popover tips" or "inline tips" - Asks about "coach marks" or "user education" - Mentions "onboarding hints" or "tip prompts" - Wants to "highlight new features" or "guide users" ## Pre-Generation Checks ### 1. Project Context Detection - [ ] Check deployment target (TipKit requires iOS 17+ / macOS 14+) - [ ] Identify if SwiftUI or UIKit project - [ ] Find App entry point location for Tips.configure() - [ ] Check for existing TipKit implementations ### 2. Conflict Detection Search for existing TipKit usage: ``` Glob: **/*Tip*.swift Grep: "import TipKit" or "Tips.configure" ``` If found, ask user: - Extend existing tip infrastructure? - Replace existing tips? ## Configuration Questions Ask user via AskUserQuestion: 1. **What features need tips?** - List the features or UI elements that should have tips - Example: "search bar, filter button, swipe-to-delete gesture" 2. **Tip presentation style?** (per tip or general preference) - Inline (TipView embedded in layout) - Popover (attached to a control) - Both 3. **Rule types needed?** - Parameter-based (show after user meets condition, e.g., has viewed a screen 3 times) - Event-based (show after user performs an action N times) - Both 4. **Display frequency?** - Immediate (tips show as soon as eligible) - Hourly - Daily - Weekly - Monthly 5. **Tip ordering?** - Independent (tips show whenever eligible) - Ordered (use TipGroup to show tips in sequence) ## Generation Process ### Step 1: Read Templates Read the templates file for code patterns: ``` Read("skills/generators/tipkit-generator/templates.md") ``` ### Step 2: Create Core Files Generate these files based on configuration: 1. `Tips/` directory with one file per tip (e.g., `SearchTip.swift`, `FilterTip.swift`) 2. `Tips/TipEvents.swift` - Centralized event definitions 3. `Tips/TipsConfiguration.swift` - Tips.configure() setup and testing utilities ### Step 3: Determine File Location Check project structure: - If `Sources/` exists -> `Sources/Tips/` - If `App/` exists -> `App/Tips/` - Otherwise -> `Tips/` ### Step 4: Integrate Tips - Add `Tips.configure()` call in App entry point - Add `TipView` or `.popoverTip()` at the appropriate view locations - Wire up event donation at action sites - Wire up tip invalidation where appropriate ## Output Format After generation, provide: ### Files Created ``` 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 ``` ### Integration Steps **App Entry Point (Required):** ```swift import TipKit @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .task { try? Tips.configure([ .displayFrequency(.daily), .datastoreLocation(.applicationDefault) ]) } } } } ``` **Inline Tip:** ```swift import TipKit struct SearchView: View { let searchTip = SearchTip() var body: some View { VStack { TipView(searchTip) SearchBar() } } } ``` **Popover Tip:** ```swift 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):** ```swift Button("Search") { performSearch() SearchTip.searchPerformed.donate() } ``` **Tip Invalidation (when tip is no longer relevant):** ```swift func onFeatureUsed() { // User discovered the feature, invalidate the tip searchTip.invalidate(reason: .actionPerformed) } ``` ### Testing Instructions 1. **Reset DataStore between runs:** ```swift // Add to a debug menu or call in preview try? Tips.resetDatastore() ``` 2. **Show all tips for testing:** ```swift // Ignores rules and frequency -- shows everything Tips.showAllTipsForTesting() ``` 3. **Show specific tips for testing:** ```swift Tips.showTipsForTesting([SearchTip.self]) ``` 4. **Test scenarios:** - Launch app fresh -- eligible tips should appear per display frequency - Perform actions that donate events -- event-based tips should appear when thresholds met - Tap tip close button -- tip should not reappear - Invalidate tip programmatically -- tip should dismiss and not reappear ## Common Gotchas 1. **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`. 2. **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. 3. **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. 4. **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. 5. **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. 6. **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). ## Tip Content Rules (WWDC23) Good tips are **actionable, instructional, and easy to remember**: - **Title** = a direct action phrase naming the feature ("Swipe to Save a Recipe"), not a greeting - **Message** = the benefit, or short instructions that stand alone without needing more context Never use tips for: - ❌ Promotion (upsells, sales, "try Pro") - ❌ Error messages or system status - ❌ Purely informational content with no action to take - ❌ Features too complex to explain in one short tip ### Eligibility and cadence specifics - **Event rules accept date modifiers**: `$0.donations.donatedWithin(.days(5)).count >= 3` — "3 times in the past 5 days", not just a lifetime count - **Associated types on event donations** scope a rule to a specific entity: donate the item's ID with the event and require N donations *for that ID*, so the tip fires on the exact item the user keeps revisiting - The **`IgnoresDisplayFrequency(true)` tip option** exempts an urgent tip from the global `.displayFrequency` cadence - The **`MaxDisplayCount(n)` tip option** auto-stops a tip after N impressions, even if it is never dismissed or invalidated ## Patterns ### Good Patterns - One tip struct per file for clarity - Centralize event definitions in a single file - Use `.actionPerformed` invalidation reason when the user completes the action the tip describes - Use `TipGroup` when tips should appear in a logical sequence - Provide a debug/testing menu that calls `Tips.resetDatastore()` - Use meaningful tip IDs that describe the feature ### Bad Patterns - Defining all tips in a single massive file - Forgetting to call `Tips.configure()` in the App entry point - Using `.immediate` display frequency in production (overwhelming users) - Hardcoding tip text instead of using localized strings for shipped apps - Creating a new tip instance to invalidate instead of using the displayed instance - Placing `TipView` inside a `ScrollView` without considering layout impact ## References - **templates.md** - Code templates for tips, rules, configuration, and TipGroup
View on GitHub