tipkit
Implement, review, or improve in-app tips and onboarding using Apple's TipKit framework. Use when adding feature discovery tooltips, onboarding flows, contextual tips, first-run experiences, coach marks, or working with Tip protocol, TipView, popoverTip, tip rules, tip events, or feature education UI.
What this skill does
# TipKit
Add feature discovery tips, contextual hints, and onboarding coach marks to
iOS 17+ apps using Apple's TipKit framework. TipKit manages display frequency,
eligibility rules, and persistence so tips appear at the right time and
disappear once the user has learned the feature.
## Contents
- [Setup](#setup)
- [Defining Tips](#defining-tips)
- [Displaying Tips](#displaying-tips)
- [Tip Rules](#tip-rules)
- [Tip Actions](#tip-actions)
- [Tip Groups](#tip-groups)
- [Programmatic Control](#programmatic-control)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## Setup
Call `Tips.configure()` once in `App.init`, before any views render. This
initializes the tips datastore and begins rule evaluation. Calling it later
risks a race where tip views attempt to display before the datastore is ready.
```swift
import SwiftUI
import TipKit
@main
struct MyApp: App {
init() {
try? Tips.configure([
.datastoreLocation(.applicationDefault)
])
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
```
### DatastoreLocation Options
| Option | Use Case |
|---|---|
| `.applicationDefault` | Default location, app sandbox (most apps) |
| `.groupContainer(identifier:)` | Share tips state across app and extensions |
| `.url(_:)` | Custom file URL for full control over storage location |
### CloudKit Sync
Sync tip state across a user's devices so they do not see the same tip on
every device. Add the CloudKit container option alongside the datastore
location.
```swift
try? Tips.configure([
.datastoreLocation(.applicationDefault),
.cloudKitContainer(.named("iCloud.com.example.app"))
])
```
## Defining Tips
Conform a struct to the `Tip` protocol. Provide a `title` at minimum.
Add `message` for supporting detail and `image` for a leading icon. Keep
titles short and action-oriented because the tip appears as a compact callout.
```swift
import TipKit
struct FavoriteTip: Tip {
var title: Text { Text("Pin Your Favorites") }
var message: Text? { Text("Tap the heart icon to save items for quick access.") }
var image: Image? { Image(systemName: "heart") }
}
```
**Properties**: `title` (required), `message` (optional detail), `image` (optional leading icon), `actions` (optional buttons), `rules` (optional eligibility conditions), `options` (display frequency, max count).
**Lifecycle**: Pending (rules unsatisfied) -> Eligible (all rules pass) -> Invalidated (dismissed, actioned, or programmatically removed). Once invalidated, a tip does not reappear unless the datastore is reset.
## Displaying Tips
### Inline Tips with TipView
Embed a `TipView` directly in your layout. It renders as a rounded card that
appears and disappears with animation. Use for tips within scrollable content.
```swift
let favoriteTip = FavoriteTip()
var body: some View {
VStack {
TipView(favoriteTip)
ItemListView()
}
}
```
### Popover Tips with .popoverTip()
Attach a tip as a popover anchored to any view. The framework draws an arrow
from the popover to the anchor. Use for tips pointing to a specific control.
```swift
Button { toggleFavorite() } label: { Image(systemName: "heart") }
.popoverTip(favoriteTip)
// Control arrow direction (omit to let system choose)
.popoverTip(favoriteTip, arrowEdge: .bottom)
```
### Custom TipViewStyle
Create a custom style to control tip appearance across the app. Conform
to `TipViewStyle` and implement `makeBody(configuration:)`.
```swift
struct CustomTipStyle: TipViewStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.image?
.font(.title2)
.foregroundStyle(.tint)
VStack(alignment: .leading) {
configuration.title
.font(.headline)
configuration.message?
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.padding()
}
}
// Apply globally or per view
TipView(favoriteTip)
.tipViewStyle(CustomTipStyle())
```
## Tip Rules
Rules control when a tip becomes eligible. All rules in the `rules` array
must pass before the tip displays. TipKit supports two rule types:
parameter-based and event-based.
### Parameter-Based Rules
Use `@Parameter` to track app state. The tip becomes eligible when the
parameter value satisfies the rule condition.
```swift
struct FavoriteTip: Tip {
@Parameter
static var hasSeenList: Bool = false
var title: Text { Text("Pin Your Favorites") }
var rules: [Rule] {
#Rule(Self.$hasSeenList) { $0 == true }
}
}
// Set the parameter when the user reaches the list
FavoriteTip.hasSeenList = true
```
### Event-Based Rules
Use `Tips.Event` to track user actions. Donate to the event each time the
action occurs. The rule fires when the donation count or timing condition
is met. This is ideal for tips that should appear after the user has
performed an action several times without discovering a related feature.
```swift
struct ShortcutTip: Tip {
static let appOpenedEvent = Tips.Event(id: "appOpened")
var title: Text { Text("Try the Quick Action") }
var rules: [Rule] {
#Rule(Self.appOpenedEvent) { $0.donations.count >= 3 }
}
}
// Donate each time the app opens
ShortcutTip.appOpenedEvent.donate()
```
### Combining Multiple Rules
Place multiple rules in the array. All must pass (logical AND).
```swift
struct AdvancedTip: Tip {
@Parameter
static var isLoggedIn: Bool = false
static let featureUsedEvent = Tips.Event(id: "featureUsed")
var title: Text { Text("Unlock Advanced Mode") }
var rules: [Rule] {
#Rule(Self.$isLoggedIn) { $0 == true }
#Rule(Self.featureUsedEvent) { $0.donations.count >= 5 }
}
}
```
### Display Frequency Options
Control how often tips appear using the `options` property.
```swift
struct DailyTip: Tip {
var title: Text { Text("Daily Reminder") }
var options: [TipOption] {
MaxDisplayCount(3) // Show at most 3 times total
IgnoresDisplayFrequency(true) // Bypass global frequency limit
}
}
```
Global display frequency is set at configuration time:
```swift
try? Tips.configure([
.displayFrequency(.daily) // .immediate, .hourly, .daily, .weekly, .monthly
])
```
With `.daily`, the system shows at most one tip per day across the entire
app, unless a specific tip sets `IgnoresDisplayFrequency(true)`.
## Tip Actions
Add action buttons to a tip for direct interaction. Each action has an `id`
and a label. Handle the action in the tip view's action handler.
```swift
struct FeatureTip: Tip {
var title: Text { Text("Try the New Editor") }
var message: Text? { Text("We added a powerful new editing mode.") }
var actions: [Action] {
Action(id: "open-editor", title: "Open Editor")
Action(id: "learn-more", title: "Learn More")
}
}
```
Handle actions in the view:
```swift
TipView(featureTip) { action in
switch action.id {
case "open-editor":
navigateToEditor()
featureTip.invalidate(reason: .actionPerformed)
case "learn-more":
showHelpSheet = true
default:
break
}
}
```
## Tip Groups
Use `TipGroup` to coordinate multiple tips within a single view.
`TipGroup` ensures only one tip from the group displays at a time,
preventing tip overload. Tips display in priority order.
```swift
struct OnboardingView: View {
let tipGroup = TipGroup(.ordered) {
WelcomeTip()
NavigationTip()
ProfileTip()
}
var body: some View {
VStack {
if let currentTip = tipGroup.currentTip {
TipView(currentTip)
}
Button("Next") {
tipGroup.currentTip?.invalidate(reason: .actionPerformed)
}
}
}
}
```
###Related in Design
contribute
IncludedLocal-only OSS contribution command center. Auto-refreshes the user's in-flight PR and issue state on invoke so conversations start with full context — no need to brief Claude on what's in flight. Helps the user find issues to contribute to on GitHub, builds per-repo dossiers of what each upstream expects (CLA, DCO, branch convention, AI policy, draft-first, review bots, issue templates), runs deterministic gates before any external action so AI-assisted contributions don't reach maintainers as slop. State is markdown-only: candidate files at ~/.contribute-system/candidates/, repo dossiers at ~/.contribute-system/research/, append-only event log at ~/.contribute-system/log.jsonl. No database, no cloud calls. Use when the user asks about their PRs / issues / contributions, wants to find new work to take on, claim an issue, build/refresh a repo's dossier, or draft a Design Issue or PR. Trigger with "/contribute", "what's my PR status", "find a contribution", "claim issue X", "draft a Design Issue for Y", "refresh dossier for Z".
architectural-analysis
IncludedUser-triggered deep architectural analysis of a codebase or scoped subtree across eight modes — information architecture, data flow, integration points, UI surfaces, interaction patterns, data model, control flow, and failure modes. This skill should be used when the user asks to "diagram this codebase," "map the architecture," "show the data flow," "give me an ERD," "trace control flow," "find the integration points," "verify the layout pattern," "audit the UX architecture," or any similar request whose primary deliverable is mermaid diagrams plus cited reports under docs/architecture/. Dispatches haiku/sonnet sub-agents in parallel for per-mode exploration, then verifies every citation mechanically before any node lands in a diagram. Not for one-off prose explanations of code (use code-explanation) or for high-level system design from scratch (use system-design).
mcp
IncludedModel Context Protocol (MCP) server development and tool management. Languages: Python, TypeScript. Capabilities: build MCP servers, integrate external APIs, discover/execute MCP tools, manage multi-server configs, design agent-centric tools. Actions: create, build, integrate, discover, execute, configure MCP servers/tools. Keywords: MCP, Model Context Protocol, MCP server, MCP tool, stdio transport, SSE transport, tool discovery, resource provider, prompt template, external API integration, Gemini CLI MCP, Claude MCP, agent tools, tool execution, server config. Use when: building MCP servers, integrating external APIs as MCP tools, discovering available MCP tools, executing MCP capabilities, configuring multi-server setups, designing tools for AI agents.
react-native-skia
IncludedDesign, build, debug, and optimise high-polish animated graphics in React Native or Expo using @shopify/react-native-skia, Reanimated, and Gesture Handler. Use when the user wants canvas-driven UI, shaders, paths, rich text, image filters, sprite fields, Skottie, video frames, snapshots, web CanvasKit setup, or performance tuning for custom motion-heavy elements such as loaders, hero art, cards, charts, progress indicators, particle systems, or gesture-driven surfaces. Also use when the user asks for fluid, glow, glass, blob, parallax, 60fps/120fps, or GPU-friendly animated effects in React Native, even if they do not explicitly say "Skia". Do not use for ordinary form/layout work with standard views.
plaid
IncludedProduct Led AI Development — guides founders from idea to launched product. Six capabilities: Idea (discover a product idea), Validate (pressure-test the idea against fatal flaws, problem reality, competition, and 2-week MVP feasibility), Plan (vision intake + document generation), Design (translate image references into a design.md spec), Launch (go-to-market strategy), and Build (roadmap execution). Use when someone says "PLAID", "plaid idea", "help me find an idea", "product idea", "idea from my business", "idea from my expertise", "plaid validate", "validate my idea", "pressure-test", "is this idea good", "find fatal flaws", "validate the problem", "plan a product", "define my vision", "generate a PRD", "product strategy", "plaid design", "design from image", "translate image to design", "create design.md", "extract design tokens", "plaid launch", "go-to-market", "launch plan", "GTM strategy", "launch playbook", "plaid build", "build the app", "start building", or "execute the roadmap".
nextjs-framer-motion-animations
IncludedAdds production-safe Motion for React or Framer Motion animations to Next.js apps, including reveal, hover and tap micro-interactions, whileInView, stagger, AnimatePresence, layout and layoutId transitions, reorder, scroll-linked UI, and lightweight route-content transitions. Use when the user asks to add, refactor, or debug Motion or Framer Motion in App Router or Pages Router codebases, especially around server/client boundaries, reduced motion, LazyMotion, bundle size, hydration, or route transitions. Avoid for GSAP-style timelines, WebGL or 3D scenes, heavy scroll storytelling, or CSS-only effects unless Motion is explicitly requested.