协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
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.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
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 impact