WWDC Quick Look 💓 By SwiftGGTeam
Make features discoverable with TipKit

Make features discoverable with TipKit

Watch original video

Highlight

TipKit makes the creation, display control and precise placement of App function prompts a matter of a few lines of code. It supports a combination of parameter rules and event rules to target target users and prevent educational information from turning into harassing pop-ups.

Core Content

There are good functions in the app, but users can’t find them—this is a dilemma that every developer encounters. The traditional approach is to create a long introduction page when you first launch it. Users will either skip it quickly or forget about it after reading it.

TipKit has a different idea: show the right tip to the right person at the right time. It covers iPhone, iPad, Mac, Apple Watch and Apple TV, and a set of APIs is common to all platforms.

Create prompt

A prompt consists of a title and a message. Use a direct action phrase for the title, such as “Collect your favorite backyard.” A message explains the benefit, such as “The favorite backyard will appear at the top of the list.” (01:53)

Tips can have icons (SF Symbol) and Action buttons attached. Icons help users create visual connections, and action buttons can jump to settings pages or guide processes.

But not all scenarios are suitable for TipKit. Advertising sales pitches, error messages, feature update notifications, complex multi-step instructions – these are not examples of user education. (02:25)

Two display methods

Popover: floating layer form, pointing to specific UI elements without changing the current page layout. Suitable for guiding users to click a button. tvOS only supports this method. (04:15)

Inline: Inline mode, the App UI automatically makes space and does not block the content. Suitable for displaying prompts in lists or forms.

Rule system

Rules are a core capability of TipKit, ensuring that tips are only shown to the people who need them most. (06:02)

Parameter-based: Persistent state based on Swift value types. For example, whether the user is logged in or whether a certain setting is turned on.

Event-based: Based on user behavior counting. For example, “The prompt will be displayed after visiting the details page 3 times.” Events can come with a time range (“Visited 3 times in the past 5 days”) and a custom correlation type (“Visited the same backyard 3 times”).

Rules can be combined. For example, “Logged in + visited the details page 3 times in the past 5 days + never favorited the backyard” - the prompt will only be displayed if all three conditions are met at the same time.

Display Control

When multiple prompts exist at the same time, the display rhythm needs to be controlled to avoid overwhelming the user. (09:57)

TipsCenterProvide global display frequency control:

  • .daily: Display at most one tip per day -.hourly: Maximum of one display per hour -.immediate: Display immediately when conditions are met, no frequency limit
  • CustomizeTimeInterval: For example, one every 5 days

Individual prompts can be set.ignoresDisplayFrequency(true)Skip global frequency limits, suitable for particularly important feature guidance. You can also set.maxDisplayCount(5)Limit the total number of impressions, which will automatically expire if exceeded.

There are also several ways to eliminate the prompt: the user performs the operation described in the prompt (callinvalidate(reason: .userPerformedAction)), reaches the maximum number of impressions, or the developer calls based on any conditionsinvalidate()。(11:27

test

TipKit provides a complete testing API, and you can see the prompt effect without meeting the rules: (12:46)

  • TipsCenter.showAllTips(): Show all tips -TipsCenter.showTips([tip1, tip2]): Only display the specified prompts -TipsCenter.hideAllTips():Hide all prompts -TipsCenter.resetDatastore(): Clear all TipKit data

These can also be configured through Xcode Scheme’s Launch Arguments to facilitate quick testing.

Detailed Content

Basic prompt definition

import TipKit

struct FavoriteBackyardTip: Tip {
    var title: Text {
        Text("Save as a Favorite")
    }
    
    var message: Text {
        Text("Your favorite backyards always appear at the top of the list.")
    }
}

Key points:

  • comply withTipAgreement, at least implementedtitleandmessage
  • titleandmessagereturnTextType, supports SwiftUI modifiers
  • The prompt definition is a value type and can be reused by creating instances globally.

Prompt with icon and action

struct FavoriteBackyardTip: Tip {
    var title: Text {
        Text("Save as a Favorite")
            .foregroundColor(.indigo)
    }
    
    var message: Text {
        Text("Your favorite backyards always appear at the top of the list.")
    }
    
    var asset: Image {
        Image(systemName: "star")
    }
    
    var actions: [Action] {
        [
            Tip.Action(
                id: "learn-more",
                title: "Learn More"
            )
        ]
    }
}

Key points:

  • assetUse SF Symbol or custom images to help users establish visual associations -actionsArray defines buttons, each Action has a unique ID and title
  • in.popoverMiniTipProcess clicks through action ID in the callback

Initial configuration

@main
struct BackyardBirdsApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }

    init() {
        TipsCenter.shared.configure()
    }
}

Key points:

  • TipsCenter.shared.configure()Must be called when app starts
  • It is responsible for the persistence of prompt status and event data
  • If not called, the event count and prompt display status will not be retained after the app is restarted.

Popover Display

private let favoriteBackyardTip = FavoriteBackyardTip()

// ...

.toolbar {
    ToolbarItem {
        Button {
            backyard.isFavorite.toggle()
        } label: {
            Label("Favorite", systemImage: "star")
                .symbolVariant(
                    backyard.isFavorite ? .fill : .none
                )
        }
        .popoverMiniTip(tip: favoriteBackyardTip)
    }
}

Key points:

  • .popoverMiniTip(tip:)It is a SwiftUI modifier that automatically attaches the prompt floating layer to the target view.
  • The hint will automatically point to the view element where the modifier is located
  • The display/hiding of tips is automatically managed by TipKit according to rules and does not require manual control

Parameter rules

struct FavoriteBackyardTip: Tip {
    @Parameter
    static var isLoggedIn: Bool = false

    var rules: Predicate<RuleInput...> {
        #Rule(Self.$isLoggedIn) { $0 == true }
    }
}

Key points:

  • @ParameterThe static properties of the tag are persistent and the value is retained after the app is restarted.
  • Set after successful loginFavoriteBackyardTip.isLoggedIn = true
  • #RuleMacros define conditions and support comparison operators and logical combinations

Event rules

struct FavoriteBackyardTip: Tip {
    @Parameter
    static var isLoggedIn: Bool = false
    
    static let enteredBackyardDetailView: Event = Event<DetailViewDonation>(
        id: "entered-backyard-detail-view"
    )

    var rules: Predicate<RuleInput...> {
        #Rule(Self.$isLoggedIn) { $0 == true }
        #Rule(Self.enteredBackyardDetailView) { $0.count >= 3 }
    }
}

// Donate the event in the view
.onAppear {
    FavoriteBackyardTip.enteredBackyardDetailView.donate()
}

Key points:

  • EventThe definition requires a unique ID and can be associated with an associated typeDonationValue
  • .donate()Called when an event occurs, TipKit automatically records the timestamp -$0.countReturns the total number of donations for this event
  • Multiple#RuleThe default is AND relationship, which must all be satisfied

Event rules with time filtering

#Rule(Self.enteredBackyardDetailView) {
    $0.donations.filter {
        $0.date > Date.now.addingTimeInterval(-5 * 60 * 60 * 24)
    }
    .count >= 3
}

Key points:

  • $0.donationsReturns all donation records, includingdateand associated data
  • usefilterFilter records within a time range
  • For time calculationaddingTimeInterval, the unit is seconds

Event rules with associated types

extension FavoriteBackyardTip {
    struct DetailViewDonation: DonationValue {
        let backyardID: Int
    }
}

// Include data when donating
.onAppear {
    FavoriteBackyardTip.enteredBackyardDetailView.donate(
        with: .init(backyardID: backyard.id)
    )
}

// Group by backyardID in the rule
#Rule(Self.enteredBackyardDetailView) {
    $0.donations.filter {
        $0.date > Date.now.addingTimeInterval(-5 * 60 * 60 * 24)
    }
    .largestSubset(by: \.backyardID)
    .count >= 3
}

Key points:

  • Association type complianceDonationValueProtocol, can store any serializable data -.largestSubset(by:)Group by specified attributes and return the number of records in the largest group
  • This example means “Visited the same backyard detail page at least 3 times in the past 5 days”
  • The smaller the associated data, the faster the query. It is recommended to only store the ID and not the complete object.

Display frequency configuration

// Global frequency: one tip per day
TipsCenter.shared.configure {
    DisplayFrequency(.daily)
}

// A single tip bypasses the frequency limit
struct FavoriteBackyardTip: Tip {
    var options: [Option] {
        [.ignoresDisplayFrequency(true)]
    }
}

// A single tip is shown at most 5 times
struct FavoriteBackyardTip: Tip {
    var options: [Option] {
        [.maxDisplayCount(5)]
    }
}

Key points:

  • The global frequency is atTipsCenterLevel configuration, affects all prompts -.ignoresDisplayFrequencyMake individual prompts independent of global restrictions -.maxDisplayCountLimit the total number of impressions of the prompt, which will automatically expire when reached.

Prompt invalid

Button {
    backyard.isFavorite.toggle()
    favoriteBackyardTip.invalidate(reason: .userPerformedAction)
} label: {
    Label("Favorite", systemImage: "star")
        .symbolVariant(backyard.isFavorite ? .fill : .none
}
.popoverMiniTip(tip: favoriteBackyardTip)

Key points:

  • .userPerformedActionIndicates that the user performed the operation described in the prompt
  • callinvalidate()The prompt disappears immediately and will not be displayed again.
  • can also be used.tipClosedIndicates that the user manually closes or customizes the reason

Core Takeaways

1. Progressive function guidance system What to do: Design a system of progressive reminders for your app’s core features so new users discover features sequentially rather than cramming all the information at once. Why it’s worth doing: TipKit’s rule system makes “showing the right tips at the right time” programmable, and the user experience is much better than traditional landing pages. How to start: List 5-10 key features of your app, create Tips for each feature, and set event rules (e.g., “Prompt for Feature B after using feature A 3 times”).

2. Intelligent user stratification What to do: Based on user behavior data, use event rules to automatically differentiate between novices, ordinary users and advanced users, and display different prompts. Why it’s worth doing: The same tip is useful for novices, but is annoying to experienced users. TipKit’s custom donation types make granular tiering possible. How to start: Define user behavior events (such as “Create the first project”, “Use advanced feature X”), record behavior details with association types, and combine them in rules to determine user stages.

3. Cross-device prompt synchronization What to do: Leverage TipKit’s iCloud sync capabilities to ensure that tips users viewed on iPhone aren’t shown twice on iPad. Why it’s worth doing: With more and more multi-device users, repeated education will reduce the quality of the product. How to get started: TipKit turns on iCloud sync by default, no additional code required. Just make sure the prompt IDs are consistent across platforms.

4. A/B testing prompt copy What to do: Prepare multiple sets of prompt copywriting for the same function, control the display ratio through event rules, and use conversion rate data to verify which set is more effective. Why it’s worth doing: The title and message of the prompt directly affect whether users are willing to try new features, and copywriting optimization has a clear ROI. How to start: Create two Tip types (e.g.FavoriteTipVariantAandFavoriteTipVariantB), use parameter rules to control the display ratio, and evaluate the effect through the click rate and function usage of the Action button.

Comments

GitHub Issues · utterances