Communitygithub.com

JordanCoin/ios-skills-collection

AppleのTipKitフレームワークを使用して、アプリ内のヒント表示やオンボーディングを実装、レビュー、改善します。機能発見のツールチップ、オンボーディングフロー、コンテキストヒント、初回実行体験、コーチマーク、またはTipプロトコル、TipView、popoverTip、tipルール、tipイベント、機能教育UIを扱う際に使用します。

ios-skills-collection とは?

このスキルは、iOS 17+向けにAppleのTipKitフレームワークを用いて機能発見のヒントやオンボーディングを実装するための包括的なガイドです。TipKitは表示頻度、表示条件の管理、永続化を自動で行い、ヒントを適切なタイミングで表示し、ユーザーが機能を学習したら非表示にします。スキルには、Tipの定義方法、TipViewやpopoverTipを使用した表示方法、パラメータベースおよびイベントベースのルールによる表示条件の制御、アクションボタンの追加、TipGroupを使った複数ヒントの調整、プログラムによる制御(無効化やテスト用ユーティリティ)が含まれます。また、よくあるミスやレビューチェックリストも提供されており、開発者が効率的かつ堅牢なTipKitの実装を行うのに役立ちます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/JordanCoin/ios-skills-collection/tree/HEAD/skills/dpearson2699--tipkit

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

TipKit

iOS 17+アプリに機能発見のヒント、コンテキストヒント、オンボーディングのコーチマークを追加します。AppleのTipKitフレームワークを使用します。TipKitは表示頻度、表示条件、永続化を管理するため、ヒントは適切なタイミングで表示され、ユーザーが機能を学習すると非表示になります。

目次

セットアップ

Tips.configure()をApp.init内で、ビューがレンダリングされる前に一度だけ呼び出します。これにより、tipデータストアが初期化され、ルールの評価が開始されます。後で呼び出すと、tipビューがデータストアの準備ができる前に表示を試みる競合状態が発生するリスクがあります。

import SwiftUI
import TipKit

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

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

DatastoreLocationオプション

オプションユースケース
.applicationDefaultデフォルトの場所、アプリサンドボックス(ほとんどのアプリ)
.groupContainer(identifier:)アプリと拡張機能間でtipの状態を共有
.url(_:)ストレージ場所を完全に制御するためのカスタムファイルURL

CloudKit同期

ユーザーのデバイス間でtipの状態を同期し、すべてのデバイスで同じtipが表示されないようにします。データストアの場所と一緒にCloudKitコンテナオプションを追加します。

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

Tipの定義

Tipプロトコルに準拠した構造体を定義します。最低でもtitleを提供します。サポートする詳細情報にはmessage、先頭のアイコンにはimageを追加します。タイトルは短くアクション指向にします。これは、tipがコンパクトなコールアウトとして表示されるためです。

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(表示頻度、最大表示回数)

ライフサイクル: 保留中(ルール未満) -> 表示可能(すべてのルール通過) -> 無効化(却下、アクション実行、またはプログラムによる削除)。一度無効化されると、データストアがリセットされない限り、tipは再表示されません。

Tipの表示

インラインTip(TipView)

TipViewをレイアウトに直接埋め込みます。アニメーション付きで表示・非表示になる角丸のカードとしてレンダリングされます。スクロール可能なコンテンツ内のtipに使用します。

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

ポップオーバーTip(.popoverTip())

任意のビューにアンカーとしてtipをポップオーバーで表示します。フレームワークはポップオーバーからアンカーに向かって矢印を描画します。特定のコントロールを指すtipに使用します。

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

// 矢印の方向を制御(省略するとシステムが選択)
.popoverTip(favoriteTip, arrowEdge: .bottom)

カスタムTipViewStyle

カスタムスタイルを作成して、アプリ全体のtipの外観を制御します。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())

Tipルール

ルールは、いつtipが表示可能になるかを制御します。rules配列内のすべてのルールが満たされる必要があります。TipKitは、パラメータベースとイベントベースの2種類のルールをサポートしています。

パラメータベースルール

@Parameterを使用してアプリの状態を追跡します。パラメータ値がルール条件を満たすと、tipが表示可能になります。

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を使用してユーザーアクションを追跡します。アクションが発生するたびにイベントに寄付(donate)します。寄付回数またはタイミング条件が満たされると、ルールが発火します。これは、ユーザーが特定のアクションを数回実行しても関連機能に気づかない場合に、ヒントを表示するのに理想的です。

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プロパティを使用して、tipが表示される頻度を制御します。

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の場合、特定のtipがIgnoresDisplayFrequency(true)を設定していない限り、システムはアプリ全体で1日あたり最大1つのtipを表示します。

Tipアクション

tipにアクションボタンを追加して、直接操作できるようにします。各アクションにはidとラベルがあります。tipビューのアクションハンドラでアクションを処理します。

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

Tipグループ

TipGroupを使用して、単一のビュー内で複数のtipを調整します。TipGroupは、グループから一度に1つのtipのみが表示されるようにし、tipの過剰表示を防ぎます。tipは優先順位に従って表示されます。

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

優先順位オプション

イニシャライザ動作
.orderedtipはリストされた順序で表示されます

現在のtipが無効化されると、グループ内の次の表示可能なtipがcurrentTipになります。

プログラムによる制御

Tipの無効化

ユーザーが発見したアクションを実行した場合、またはtipが不要になった場合に、invalidate(reason:)を呼び出します。

let tip = FavoriteTip()
tip.invalidate(reason: .actionPerformed)
理由使用タイミング
.actionPerformedユーザーがtipで説明されているアクションを実行した
.displayCountExceededtipが最大表示回数に達した
.tipClosedユーザーが明示的にtipを閉じた

テスト用ユーティリティ

TipKitには、開発中およびテスト中にtipの表示を制御するための静的メソッドが用意されています。これらは#if DEBUGまたはProcessInfoチェックの背後にゲートして、本番ビルドで実行されないようにします。

#if DEBUG
// ルールに関係なくすべてのtipを表示(開発中に便利)
Tips.showAllTipsForTesting()

// 特定のtipのみを表示
Tips.showTipsForTesting([FavoriteTip.self, ShortcutTip.self])

// すべてのtipを非表示(tipを含まないUIテストに便利)
Tips.hideAllTipsForTesting()

// データストアをリセット(すべてのtipの状態、無効化、イベントをクリア)
try? Tips.resetDatastore()
#endif

テストスキームでのProcessInfoの使用

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

Xcodeスキームの起動引数として--show-all-tipsを渡します(開発ビルド用)。

よくあるミス

してはいけないこと: Tips.configure()をApp.init以外で呼び出す

ビューのonAppearまたはtask修飾子でTips.configure()を呼び出すと、tipビューがデータストアの準備ができる前にレンダリングしようとする競合状態が発生し、tipが欠落したりちらついたりします。

// 間違い
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() } }
}

してはいけないこと: 一度に多くのtipを表示する

複数のtipを同時に表示すると、ユーザーを圧倒し、各tipの効果が薄れます。ユーザーはそれらを無視するようになります。

// 間違い: 3つのtipが同時に表示される
VStack {
    TipView(tipA)
    TipView(tipB)
    TipView(tipC)
}

// 正しい: TipGroupを使用して順序付ける
let group = TipGroup(.ordered) { TipA(); TipB(); TipC() }
if let currentTip = group.currentTip {
    TipView(currentTip)
}

してはいけないこと: ユーザーがアクションを実行した後にtipを無効化するのを忘れる

tipに「星をタップしてお気に入りに追加」と表示され、ユーザーが星をタップしたのにtipが残っている場合、UIへの信頼が損なわれます。

// 間違い: ユーザーがアクションを実行した後もtipが表示されたまま
Button("お気に入り") { toggleFavorite() }
    .popoverTip(favoriteTip)

// 正しい: アクション時に無効化
Button("お気に入り") {
    toggleFavorite()
    favoriteTip.invalidate(reason: .actionPerformed)
}
.popoverTip(favoriteTip)

してはいけないこと: テスト用tipを本番環境で有効にしたままにする

Tips.showAllTipsForTesting()はすべてのルールと頻度制限をバイパスします。これを本番環境に出荷すると、すべてのユーザーがすべてのtipをすぐに目にすることになります。

// 間違い: 常に有効
Tips.showAllTipsForTesting()

// 正しい: DEBUGの後ろにゲート
#if DEBUG
Tips.showAllTipsForTesting()
#endif

してはいけないこと: tipのタイトルを長くしすぎる

長いタイトルは、コンパクトなtipコールアウト内で切り詰められたり、不格好に折り返されたりします。主要なアクションはタイトルに、補足的なコンテキストはメッセージに入れます。

// 間違い
var title: Text { Text("ハートボタンをタップして、このアイテムをお気に入りリストに保存できます") }

// 正しい
var title: Text { Text("お気に入りに保存") }
var message: Text? { Text("ハートアイコンをタップして、アイテムをすぐにアクセスできるように保存します。") }

してはいけないこと: 重要な情報にtipを使用する

ユーザーはいつでもtipを閉じることができ、再表示されません。重要な指示や安全情報をtipに入れてはいけません。

// 間違い: 閉じられるtipに重要な情報
struct DataLossTip: Tip {
    var title: Text { Text("保存されていない変更は失われます") }
}

// 正しい: 重要な情報にはアラートまたはインライン警告を使用する
// tipは機能発見とプログレッシブディスクロージャーに限定する

レビューチェックリスト

  • Tips.configure()がApp.initで、すべてのビューがレンダリングされる前に呼び出されている
  • 各tipに明確で簡潔なタイトルがある(アクション指向、約40文字以内)
  • ユーザーが発見したアクションを実行したときにtipが無効化される
  • ルールが適切に設定され、tipが適切なタイミングで表示される(すべてのtipが初回起動時にすぐに表示されるわけではない)
  • 1つのビューに複数のtipがある場合、TipGroupが使用されている
  • テスト用ユーティリティ(showAllTipsForTesting、resetDatastore)が#if DEBUGの後ろにゲートされている
  • アプリが複数のデバイスをサポートする場合、CloudKit同期が設定されている
  • 表示頻度が適切に設定されている(ほとんどのアプリでは.dailyまたは.weekly)
  • tipは機能発見にのみ使用され、重要な情報には使用されていない
  • デフォルトのスタイルがアプリのデザインに合わない場合、カスタムTipViewStyleが一貫して適用されている
  • tipアクションが処理され、アクションハンドラでtipが無効化されている
  • イベントへの寄付が正しいユーザーアクションポイントに配置されている
  • カスタムTip型がSendableであることを確認し、Tipsは@MainActor上で設定する

参考資料

  • 完全な実装パターン(カスタムスタイル、イベントベースルール、tipグループ、テスト戦略、オンボーディングフロー、SwiftUIプレビュー設定を含む)については、references/tipkit-patterns.mdを参照してください。

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.

関連スキル