Communitygithub.com

JordanCoin/ios-skills-collection

使用 Apple 的 TipKit 框架,實作、審查或改進應用程式內的提示引導與新手引導流程。適用於新增功能探索提示、引導流程、情境提示、首次使用體驗、操作指引標記,或使用 Tip 協定、TipView、popoverTip、tip 規則、tip 事件及功能教育 UI。

ios-skills-collection 是什麼?

這項技能提供開發者使用 Apple TipKit 框架(適用於 iOS 17+)在應用程式中加入功能探索提示、上下文提示與引導操作標記的完整指南。技能文件涵蓋了完整的實作流程:從在 `App.init` 中設定 `Tips.configure()` 以初始化資料儲存庫,到定義符合 `Tip` 協定的結構(包括 `title`、`message`、`image`、`actions`、`rules` 與 `options` 等屬性),以及多種顯示方式,如內嵌式 `TipView` 與錨定於特定控制項的 `.popoverTip()` 修飾器。文件深入說明了兩種規則類型:基於 `@Parameter` 的參數規則與基於 `Tips.Event` 的事件規則,可用於控制提示的顯示時機。同時也介紹了 `TipGroup` 來協調單一視圖中的多個提示、提示動作按鈕的處理,以及透過 `invalidate(reason:)` 與測試工具進行程式化控制。更重要的是,文件列出了常見錯誤,例如在 `App.init` 之外呼叫設定、同時顯示過多提示、忘記在使用者執行操作後使提示失效,以及將測試用提示啟用於正式版本中,並提供了一份審查清單以確保實作品質。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/JordanCoin/ios-skills-collection/tree/HEAD/skills/dpearson2699--tipkit

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

TipKit

使用 Apple 的 TipKit 框架,為 iOS 17+ 應用程式加入功能探索提示、上下文提示與引導操作標記。TipKit 會管理顯示頻率、符合資格規則與持久性,讓提示在正確時機出現,並在使用者學會功能後消失。

目錄

設定

在 App.init 中,於任何視圖渲染之前,呼叫 Tips.configure() 一次。這會初始化提示的資料儲存庫並開始規則評估。若在之後才呼叫,可能會發生提示視圖在資料儲存庫準備好之前就嘗試顯示的競爭情況。

import SwiftUI
import TipKit

@main
struct MyApp: App {
    init() {
        try? Tips.configure([
            .datastoreLocation(.applicationDefault)
        ])
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

DatastoreLocation 選項

選項使用情境
.applicationDefault預設位置,應用程式沙盒(適用於多數應用程式)
.groupContainer(identifier:)在應用程式與其擴充功能之間共享提示狀態
.url(_:)自訂檔案 URL,完全控制儲存位置

CloudKit 同步

跨使用者的裝置同步提示狀態,避免使用者在每台裝置上都看到相同的提示。將 CloudKit 容器選項與資料儲存庫位置一起加入。

try? Tips.configure([
    .datastoreLocation(.applicationDefault),
    .cloudKitContainer(.named("iCloud.com.example.app"))
])

定義提示

建立一個符合 Tip 協定的結構。至少需要提供 title。並可加入 message 作為輔助細節,以及 image 作為前置圖示。保持標題簡短且以行動為導向,因為提示會以緊湊的呼叫輸出形式出現。

import TipKit

struct FavoriteTip: Tip {
    var title: Text { Text("釘選你的最愛") }
    var message: Text? { Text("點擊愛心圖示即可儲存項目以供快速存取。") }
    var image: Image? { Image(systemName: "heart") }
}

屬性: title(必要)、message(選擇性細節)、image(選擇性前置圖示)、actions(選擇性按鈕)、rules(選擇性符合資格條件)、options(顯示頻率、最大次數)。

生命週期: 待定(規則未滿足)-> 符合資格(所有規則通過)-> 失效(被關閉、執行動作或程式化移除)。一旦失效,除非重設資料儲存庫,否則提示不會再次出現。

顯示提示

內嵌提示 TipView

將 TipView 直接嵌入你的佈局中。它會渲染成一個圓角卡片,並以動畫效果出現和消失。適用於可滾動內容中的提示。

let favoriteTip = FavoriteTip()
var body: some View {
    VStack {
        TipView(favoriteTip)
        ItemListView()
    }
}

彈出提示 .popoverTip()

將提示作為彈出視窗,錨定在任何視圖上。框架會繪製一條從彈出視窗指向錨定物的箭頭。適用於指向特定控制項的提示。

Button { toggleFavorite() } label: { Image(systemName: "heart") }
    .popoverTip(favoriteTip)

// 控制箭頭方向(省略則由系統選擇)
.popoverTip(favoriteTip, arrowEdge: .bottom)

自訂 TipViewStyle

建立自訂樣式以控制整個應用程式的提示外觀。遵循 TipViewStyle 並實作 makeBody(configuration:)。

struct CustomTipStyle: TipViewStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack(spacing: 12) {
            configuration.image?
                .font(.title2)
                .foregroundStyle(.tint)

            VStack(alignment: .leading, spacing: 4) {
                configuration.title
                    .font(.headline)
                configuration.message?
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }
        }
        .padding()
    }
}

// 全局應用或逐視圖應用
TipView(favoriteTip)
    .tipViewStyle(CustomTipStyle())

提示規則

規則控制提示何時符合資格。rules 陣列中的所有規則都必須通過,提示才會顯示。TipKit 支援兩種規則類型:基於參數和基於事件。

基於參數的規則

使用 @Parameter 來追蹤應用程式狀態。當參數值滿足規則條件時,提示即符合資格。

struct FavoriteTip: Tip {
    @Parameter
    static var hasSeenList: Bool = false

    var title: Text { Text("釘選你的最愛") }

    var rules: [Rule] {
        #Rule(Self.$hasSeenList) { $0 == true }
    }
}

// 當使用者到達列表時設定參數
FavoriteTip.hasSeenList = true

基於事件的規則

使用 Tips.Event 來追蹤使用者動作。每次動作發生時貢獻給該事件。當貢獻次數或時間條件滿足時,規則就會觸發。這非常適合用於提示應該在使用者執行某個動作數次但尚未發現相關功能後才出現的情況。

struct ShortcutTip: Tip {
    static let appOpenedEvent = Tips.Event(id: "appOpened")

    var title: Text { Text("試試快速動作") }

    var rules: [Rule] {
        #Rule(Self.appOpenedEvent) { $0.donations.count >= 3 }
    }
}

// 每次應用程式開啟時貢獻
ShortcutTip.appOpenedEvent.donate()

組合多個規則

將多個規則放入陣列中。所有規則都必須通過(邏輯 AND)。

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

    static let featureUsedEvent = Tips.Event(id: "featureUsed")

    var title: Text { Text("解鎖進階模式") }

    var rules: [Rule] {
        #Rule(Self.$isLoggedIn) { $0 == true }
        #Rule(Self.featureUsedEvent) { $0.donations.count >= 5 }
    }
}

顯示頻率選項

使用 options 屬性控制提示的顯示頻率。

struct DailyTip: Tip {
    var title: Text { Text("每日提醒") }

    var options: [TipOption] {
        MaxDisplayCount(3)                   // 最多總共顯示 3 次
        IgnoresDisplayFrequency(true)        // 繞過全局頻率限制
    }
}

全局顯示頻率在設定時指定:

try? Tips.configure([
    .displayFrequency(.daily)  // .immediate, .hourly, .daily, .weekly, .monthly
])

使用 .daily 時,系統每天在整個應用程式中最多顯示一個提示,除非特定提示設定了 IgnoresDisplayFrequency(true)。

提示動作

為提示加入動作按鈕以進行直接互動。每個動作都有一個 id 和一個標籤。在提示視圖的動作處理器中處理該動作。

struct FeatureTip: Tip {
    var title: Text { Text("試試新的編輯器") }
    var message: Text? { Text("我們新增了一個強大的新編輯模式。") }

    var actions: [Action] {
        Action(id: "open-editor", title: "開啟編輯器")
        Action(id: "learn-more", title: "了解更多")
    }
}

在視圖中處理動作:

TipView(featureTip) { action in
    switch action.id {
    case "open-editor":
        navigateToEditor()
        featureTip.invalidate(reason: .actionPerformed)
    case "learn-more":
        showHelpSheet = true
    default:
        break
    }
}

提示群組

使用 TipGroup 來協調單一視圖中的多個提示。TipGroup 確保一次只顯示群組中的一個提示,防止提示過載。提示會依照優先順序顯示。

struct OnboardingView: View {
    let tipGroup = TipGroup(.ordered) {
        WelcomeTip()
        NavigationTip()
        ProfileTip()
    }

    var body: some View {
        VStack {
            if let currentTip = tipGroup.currentTip {
                TipView(currentTip)
            }

            Button("下一步") {
                tipGroup.currentTip?.invalidate(reason: .actionPerformed)
            }
        }
    }
}

優先順序選項

初始化器行為
.ordered提示依照列出的順序顯示

當目前的提示失效後,群組中下一個符合資格的提示會成為 currentTip。

程式化控制

使提示失效

當使用者執行了提示描述的動作,或提示不再相關時,呼叫 invalidate(reason:)。

let tip = FavoriteTip()
tip.invalidate(reason: .actionPerformed)
原因使用時機
.actionPerformed使用者執行了提示描述的動作
.displayCountExceeded提示已達到最大顯示次數
.tipClosed使用者明確關閉了提示

測試工具

TipKit 提供了靜態方法來控制在開發和測試期間的提示可見性。將它們放在 #if DEBUG 或 ProcessInfo 檢查之後,確保它們永遠不會在正式建置中執行。

#if DEBUG
// 顯示所有提示,無論規則為何(在開發期間很有用)
Tips.showAllTipsForTesting()

// 僅顯示特定提示
Tips.showTipsForTesting([FavoriteTip.self, ShortcutTip.self])

// 隱藏所有提示(對於不涉及提示的 UI 測試很有用)
Tips.hideAllTipsForTesting()

// 重設資料儲存庫(清除所有提示狀態、失效記錄和事件)
try? Tips.resetDatastore()
#endif

使用 ProcessInfo 進行測試方案

if ProcessInfo.processInfo.arguments.contains("--show-all-tips") {
    Tips.showAllTipsForTesting()
}

在開發建置的 Xcode 方案中,將 --show-all-tips 作為啟動引數傳遞。

常見錯誤

不要:在 App.init 以外的任何地方呼叫 Tips.configure()

在視圖的 onAppear 或 task 修飾器中呼叫 Tips.configure() 會產生競爭情況,導致提示視圖在資料儲存庫準備好之前就嘗試渲染,造成提示遺失或閃爍。

// 錯誤
struct ContentView: View {
    var body: some View {
        Text("Hello")
            .task { try? Tips.configure() }  // 太遲了,視圖已渲染
    }
}

// 正確
@main struct MyApp: App {
    init() { try? Tips.configure() }
    var body: some Scene { WindowGroup { ContentView() } }
}

不要:一次顯示太多提示

同時顯示多個提示會讓使用者感到不知所措,並削弱每個提示的影響力。使用者會學會忽略它們。

// 錯誤:三個提示同時可見
VStack {
    TipView(tipA)
    TipView(tipB)
    TipView(tipC)
}

// 正確:使用 TipGroup 將它們排序
let group = TipGroup(.ordered) { TipA(); TipB(); TipC() }
if let currentTip = group.currentTip {
    TipView(currentTip)
}

不要:忘記在使用者執行動作後使提示失效

如果提示說「點擊星號加入最愛」,而使用者點擊了星號但提示仍然存在,這會削弱使用者對 UI 的信任。

// 錯誤:使用者操作後提示仍然可見
Button("最愛") { toggleFavorite() }
    .popoverTip(favoriteTip)

// 正確:在操作時使提示失效
Button("最愛") {
    toggleFavorite()
    favoriteTip.invalidate(reason: .actionPerformed)
}
.popoverTip(favoriteTip)

不要:在正式版本中啟用測試提示

Tips.showAllTipsForTesting() 會繞過所有規則和頻率限制。如果在正式版本中啟用,每個使用者都會立即看到所有提示。

// 錯誤:始終啟用
Tips.showAllTipsForTesting()

// 正確:放在 DEBUG 後面
#if DEBUG
Tips.showAllTipsForTesting()
#endif

不要:讓提示標題過長

長標題在緊湊的提示呼叫輸出中會被截斷或怪異地換行。將關鍵動作放在標題中,將輔助上下文放在訊息中。

// 錯誤
var title: Text { Text("你可以點擊愛心按鈕將此項目儲存到你的最愛清單") }

// 正確
var title: Text { Text("儲存到最愛") }
var message: Text? { Text("點擊愛心圖示即可保留項目以供快速存取。") }

不要:將提示用於關鍵資訊

使用者可以隨時關閉提示,且關閉後不會再次出現。絕對不要將必要的說明或安全資訊放在提示中。

// 錯誤:關鍵資訊放在可關閉的提示中
struct DataLossTip: Tip {
    var title: Text { Text("未儲存的變更將會遺失") }
}

// 正確:對關鍵資訊使用警示或內聯警告
// 保留提示給功能探索和漸進式揭露

審查清單

  • Tips.configure() 在 App.init 中呼叫,且在渲染任何視圖之前
  • 每個提示都有清晰簡潔的標題(以行動為導向,約 40 個字元以內)
  • 當使用者執行提示描述的動作時,提示已失效
  • 設定了規則,以便提示在正確時機出現(而非所有提示都在首次啟動時立即顯示)
  • 當一個視圖中存在多個提示時,使用了 TipGroup
  • 測試工具(showAllTipsForTesting、resetDatastore)放在 #if DEBUG 後面
  • 如果應用程式支援多個裝置,已設定 CloudKit 同步
  • 顯示頻率設定適當(大多數應用程式使用 .daily 或 .weekly)
  • 提示僅用於功能探索,而非關鍵資訊
  • 如果預設樣式不符合應用程式設計,則一致地套用了自訂 TipViewStyle
  • 已處理提示動作,並在動作處理器中使提示失效
  • 事件貢獻已放置在正確的使用者動作點
  • 確保自訂 Tip 類型符合 Sendable;在 @MainActor 上設定 Tips

參考資料

  • 參閱 references/tipkit-patterns.md 了解完整的實作模式,包含自訂樣式、基於事件的規則、提示群組、測試策略、引導流程及 SwiftUI 預覽設定。

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

JordanCoin/ios-skills-collection

Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries. Use when the user asks about GitHub issues, pull requests, workflows, or wants to interact with GitHub repositories from the command line — including tasks like check CI status, create PR, list issues, or query the GitHub API.

JordanCoin/ios-skills-collection

Create media playback experiences using AVKit. Use when adding video players with AVPlayerViewController, enabling Picture-in-Picture, routing media with AirPlay, using SwiftUI VideoPlayer views, configuring transport controls, displaying subtitles and closed captions, or integrating AVFoundation playback with system UI.

JordanCoin/ios-skills-collection

Integrate and optimize Core ML models in iOS apps for on-device machine learning inference. Covers model loading (.mlmodelc, .mlpackage), predictions with auto-generated classes and MLFeatureProvider, compute unit configuration (CPU, GPU, Neural Engine), MLTensor, VNCoreMLRequest, MLComputePlan, multi-model pipelines, and deployment strategies. Use when loading Core ML models, making predictions, configuring compute units, or profiling model performance.

JordanCoin/ios-skills-collection

Implement, review, or improve maps and location features in iOS/macOS apps using MapKit and CoreLocation. Use when working with Map views, annotations, markers, polylines, user location tracking, geocoding, reverse geocoding, search/autocomplete, directions and routes, geofencing, region monitoring, CLLocationUpdate async streams, or location authorization flows. Also use when working with maps, coordinates, addresses, places, directions, distance calculations, or location-based features in Swift apps.

JordanCoin/ios-skills-collection

Display and manipulate PDF documents using PDFKit. Use when embedding PDFView to show PDF files, creating or modifying PDFDocument instances, adding annotations (highlights, notes, signatures), extracting text with PDFSelection, navigating pages, generating thumbnails, filling PDF forms, or wrapping PDFView in SwiftUI.

JordanCoin/ios-skills-collection

When the user wants to optimize their Google Play Store listing — title, short description, full description, keywords, ratings, or Play Store-specific features. Use when the user mentions "Google Play", "Android", "Play Store", "Play Console", "short description", "full description indexed", "Google Play ASO", or wants Google Play-specific keyword, creative, or ratings strategy. For iOS App Store optimization, see aso-audit and metadata-optimization.

JordanCoin/ios-skills-collection

When the user wants to implement, optimize, or use App Clips for app discovery and conversion. Use when the user mentions "App Clip", "app clip code", "mini app", "instant app", "App Clip card", "App Clip link", "no download required", "instant experience", or wants to understand how App Clips appear in App Store search. For general App Store discoverability, see aso-audit. For marketing campaigns, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to plan a launch strategy for a new app or major update. Also use when the user mentions "app launch", "launch plan", "launch checklist", "pre-launch", "launch day", or "how to launch my app". For ongoing ASO after launch, see aso-audit. For paid acquisition during launch, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to analyze their own app's actual performance data from App Store Connect — real downloads, revenue, IAP, subscriptions, trials, or country breakdowns synced via Appeeky Connect. Use when the user asks about "my downloads", "my revenue", "how is my app performing", "ASC data", "sales and trends", "my subscription numbers", "App Store Connect metrics", or wants to compare periods or top markets. For third-party app estimates, see app-analytics. For subscription analytics depth, see monetization-strategy.

JordanCoin/ios-skills-collection

When the user wants a full ASO health audit, review their App Store listing quality, or diagnose why their app isn't ranking. Also use when the user mentions "ASO audit", "ASO score", "why am I not ranking", "listing review", or "optimize my app store page". For keyword-specific research, see keyword-research. For metadata writing, see metadata-optimization.

JordanCoin/ios-skills-collection

Always use when working on any iOS, macOS, Swift, SwiftUI, Xcode, or Apple platform project. Routes to the correct specialized skills based on development phase and task context. This collection contains 210 skills from 28 authors covering the full iOS lifecycle.

相關技能