SuperAPI AI Directory

tipkit

Coding & development

View repository

Updated 10-16 05:53

About this skill

TipKit

Use TipKit for small, contextual feature-discovery moments: inline tips, popover tips, rule-gated education, and lightweight coach marks. Keep generic SwiftUI navigation, layout, and long first-run onboarding flows in their sibling skills.

Contents

  • Availability & Configuration
  • Defining Tips
  • Presenting Tips
  • Rules & Events
  • Tip Groups (iOS 18+)
  • Testing Overrides
  • Common Mistakes
  • Review Checklist
  • References

Availability & Configuration

Core TipKit (Tip, TipView, popoverTip, rules, events) is iOS 17+. Gate iOS 18+ APIs (TipGroup, .cloudKitContainer, MaxDisplayDuration) and iOS 26+ APIs (resetEligibility()) explicitly.

Call Tips.configure(_:) once during app startup in init() or application(_:didFinishLaunchingWithOptions:):

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() }
    }
}

For app groups sharing tip state, use .groupContainer(identifier:). On iOS 18+, sync tip state across devices by adding .cloudKitContainer(.named("iCloud.com.example.tips")).

Defining Tips

Create tips by conforming to the Tip protocol:

struct BookmarkTip: Tip {
    var title: Text { Text("Save for Later") }
    var message: Text? { Text("Tap to bookmark your favorite articles.") }
    var image: Image? { Image(systemName: "bookmark") }

    @Parameter
    static var hasViewedArticle: Bool = false

    static let bookmarkEvent = Event(id: "didBookmarkArticle")

    var rules: [Rule] {
        #Rule(Self.$hasViewedArticle) { $0 == true }
        #Rule(Self.bookmarkEvent) { $0.donations.count < 3 }
    }

    var actions: [Action] {
        Action(id: "learn-more", title: "Learn More") {
            // Action handler
        }
    }
}

Presenting Tips

Present tips inline within layouts or anchored as popovers:

// Inline presentation
TipView(BookmarkTip(), arrowEdge: .bottom)

// Popover presentation anchored to a control
Button("Bookmark", systemImage: "bookmark") {
    saveBookmark()
    BookmarkTip.bookmarkEvent.donate()
}
.popoverTip(BookmarkTip(), arrowEdge: .top)

Invalidate tips when the user completes the taught action: BookmarkTip().invalidate(reason: .actionPerformed).

Rules & Events

  • Parameters (#Rule(Self.$param)): Persistent state flags representing user characteristics or settings.
  • Events (Event(id:)): Track user interactions. Donate via event.donate(). Use #Rule(event) { $0.donations.count == 0 } for frequency or completion gating.

Tip Groups (iOS 18+)

Coordinate multiple tips without cluttering the screen. Store TipGroup in @State:

struct OnboardingView: View {
    @State private var tips = TipGroup(.ordered) {
        WelcomeTip()
        SearchTip()
        FilterTip()
    }

    var body: some View {
        VStack {
            TipView(tips.currentTip)
            ContentView()
        }
    }
}

Testing Overrides

Use testing overrides only in debug builds, before calling Tips.configure():

#if DEBUG
if ProcessInfo.processInfo.arguments.contains("--reset-tips") {
    try? Tips.resetDatastore()
}
if ProcessInfo.processInfo.arguments.contains("--show-all-tips") {
    Tips.showAllTipsForTesting()
}
#endif
try Tips.configure()

Launch arguments: -com.apple.TipKit.ResetDatastore 1, -com.apple.TipKit.ShowAllTips 1.

Common Mistakes

  • Configuring TipKit from a view: Calling Tips.configure() in onAppear races with view layout and can throw datastore errors. Configure in app init().
  • Using tips for critical information: Tips are dismissible educational hints. Use alerts or inline banners for errors and destructive warnings.
  • Shipping testing overrides to production: showAllTipsForTesting() bypasses display rules. Keep overrides behind #if DEBUG.
  • Ungated iOS 18+ APIs: Always gate TipGroup, .cloudKitContainer, and MaxDisplayDuration with #available(iOS 18, *).
  • Unstable reusable tip IDs: If dynamic tips do not provide stable id properties, TipKit cannot track persistence and invalidation correctly.

Review Checklist

  • Tips.configure(_:) invoked once at app launch before any tip displays
  • iOS 18+ features (TipGroup, CloudKit container) guarded with availability checks
  • Tip copy is concise, actionable, and focused on discovery
  • Events donated when user performs the action; tip invalidated upon completion
  • TipGroup stored in @State when sequencing related tips
  • Testing overrides isolated to debug schemes or UI test arguments

References