mcpField
T
tipkit

tipkit

github.com/dpearson2699デザイン・UI1.2k3.6k 回インストール13 週間前に更新
リポジトリ

tipkit について

Apple プラットフォームのアプリに、状況に応じた機能紹介を追加する TipKit のスキルです。表示方法、適用ルール、状態の保存、テスト、API の利用可能条件を扱い、短く適切なタイミングでヒントを提示する方法を説明します。

主な機能

  1. 01インラインとポップオーバーでのヒント表示
  2. 02適用ルールとイベント記録
  3. 03表示上限とヒントの無効化
  4. 04ヒントグループと順序付き表示
  5. 05CloudKit と App Group の状態設定
  6. 06テスト用上書きと API 利用条件

活用シーン

  1. 01紹介する機能の近くに操作ヒントを表示
  2. 02指定したユーザー操作後だけヒントを提示
  3. 03複数の機能紹介ヒントを順番に表示
  4. 04デバッグビルドでヒントの表示条件を検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill tipkit

インストール後、Claude Code で/tipkitを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonreferences/tipkit-patterns.md

詳細

TipKit

インラインヒント、ポップオーバーヒント、ルールに応じたガイド、軽量なコーチマークなど、機能を見つけてもらうための小さなコンテキストに応じた案内には TipKit を使用します。汎用的な SwiftUI のアーキテクチャ、ナビゲーション、レイアウト、長い初回オンボーディングフローについては、TipKit による表示が中心的な課題でない限り、関連する別のスキルを参照してください。

目次

利用可能な環境

TipKit の基本機能である Tip、TipView、popoverTip、ルール、イベント、オプション、テスト用のオーバーライドは、iOS 17 以降、iPadOS 17 以降、macOS 14 以降、tvOS 17 以降、watchOS 10 以降、visionOS 1 以降で利用できます。

新しい API は明示的に利用可能な環境を確認してください。

API利用可能な環境用途
TipGroupiOS 18 以降ヒントをグループ化または順番に表示します。ヒントグループの判断基準を適用してください。
.cloudKitContainer(...)iOS 18 以降デバイス間でヒントの状態、パラメーター、イベント、表示回数を同期します。
MaxDisplayDurationiOS 18 以降累積表示時間に応じて自動的に無効化します。
resetEligibility()iOS 26 以降データストアをリセットせずに、以前に無効化したヒントを再び表示対象にします。

TipKit を設定する

ヒントが表示される前に、アプリの初期化時に Tips.configure(_:) を一度呼び出します。ビューの onAppear や .task から TipKit を設定しないでください。

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

アプリと拡張機能、または App Group のメンバー間でヒントの状態を意図的に共有する場合に限り、.datastoreLocation(.groupContainer(identifier:)) を使用します。TipKit はヒントのレコードとともにオプションの状態を永続化するため、App Group のメンバー間でオプション設定を統一してください。

CloudKit 同期

CloudKit 同期は iOS 18 以降でのみ使用します。iCloud と CloudKit、および「バックグラウンドモード」>「リモート通知」を有効にしてから、コンテナを渡してください。

try Tips.configure([
    .cloudKitContainer(.named("iCloud.com.example.app.tips"))
])

.tips 接尾辞の付いた専用コンテナを推奨します。.automatic は、利用資格のある .tips コンテナが存在すればそれを使用し、なければプライマリコンテナにフォールバックします。

適切なヒントをデザインする

ヒントは、小さく一時的な案内です。簡単な手順をいくつか試すだけで理解できる機能に使用します。長い説明や複数の画面が必要な場合、または重要な安全上の情報やエラー情報を伝える場合は、代わりにチュートリアル、アラート、インライン警告、オンボーディングフローを使用してください。

HIG に沿ったデフォルトに従ってください。

  • タイトルは短く、直接的で、行動を促すものにします。
  • 1~2 文にまとめ、宣伝的な内容や無関係な文言は避けます。
  • 説明する機能の近くにヒントを配置します。
  • 近くの UI を隠すと作業の妨げになる場合は、インラインヒントを優先します。
  • 現在のレイアウトを維持することが重要で、特定のコントロールを指し示せる場合は、ポップオーバーヒントを優先します。
  • 適切な対象者にだけヒントが表示されるよう、ルールと表示頻度を設定します。
  • ポップオーバーがすでにアイコンを指し示している場合は、ヒント内でアイコンを繰り返すのを避けます。

ヒントを定義する

Tip は Identifiable と Sendable に準拠します。最低限 title を指定し、機能を見つけてもらう助けになる場合に限り、message、image、actions、rules、options、id を追加します。

import TipKit

struct FavoriteTip: Tip {
    var title: Text { Text("Save to Favorites") }
    var message: Text? { Text("Tap the heart to keep items for quick access.") }
    var image: Image? { Image(systemName: "heart.fill") }
}

デフォルトでは、TipKit はヒントの型名を id として使用します。永続化される状態をコンテンツごとに変える必要がある再利用可能なヒントでは、id を上書きしてください。

struct NewItemTip: Tip {
    let itemID: Item.ID

    var id: String { "NewItemTip-\(itemID)" }
    var title: Text { Text("New Item Available") }
}

安定した具体的な識別子を使用します。一時的な文言や不安定な順序から ID を生成しないでください。

ヒントを表示する

インラインヒントには TipView を使用します。

let favoriteTip = FavoriteTip()

VStack {
    TipView(favoriteTip, arrowEdge: .bottom)
    ItemListView()
}

ヒントでコントロールを指し示す場合は .popoverTip を使用します。

Button {
    toggleFavorite()
    favoriteTip.invalidate(reason: .actionPerformed)
} label: {
    Image(systemName: "heart")
}
.popoverTip(favoriteTip, arrowEdge: .top)

ルールとイベント

ルールは AND で結合されます。すべてのルールを満たした場合にのみ、ヒントが表示対象になります。

永続化されるアプリの状態には @Parameter を使用します。

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

    var title: Text { Text("Save to Favorites") }

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

ユーザーの操作が繰り返される場合は Tips.Event を使用します。TipKit はデフォルトで直近 1000 件の寄付をクエリするため、イベントルールは意図を明確にし、範囲を限定してください。

struct ShortcutTip: Tip {
    static let manualSaveEvent = Tips.Event(id: "manualSave")

    var title: Text { Text("Save Faster") }

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

ShortcutTip.manualSaveEvent.sendDonation()

より複雑なイベントルールでは、DonationInfo: Codable, Sendable を満たす Tips.Event<DonationInfo> を定義します。寄付のペイロードは小さく保ってください。

複数のヒントで同じイベントを使用する場合は、関連するイベント定義を共有の名前空間にまとめます。イベント ID は永続化の境界となるため、衝突すると表示対象の判定が分かりにくくなることがあります。

オプションと無効化

オプションは必要な場合にのみ使用します。表示頻度と無効化ルールは、ヒントの永続化される動作の一部です。

struct DailyTip: Tip {
    var title: Text { Text("Try Filters") }

    var options: [any TipOption] {
        MaxDisplayCount(3)
        IgnoresDisplayFrequency(false)
    }
}

MaxDisplayDuration は iOS 18 以降で使用できます。累積表示時間をカウントし、自動無効化が発生するまでに必要な最小連続表示時間があります。アプリが説明した操作や順序付きステップの完了を把握している場合は、明示的な invalidate(reason:) の代わりに使用しないでください。

ユーザーが見つけた操作を実行したとき、またはヒントが不要になったときに invalidate(reason:) を呼び出します。データストアがリセットされるまで、または iOS 26 以降では該当するヒントで await resetEligibility() が呼び出されるまで、無効化は取り消せません。

favoriteTip.invalidate(reason: .actionPerformed)

明示的な終了には .tipClosed を使用します。.displayCountExceeded や .displayDurationExceeded は、自動無効化の結果を説明する場合にのみ使用してください。

アクションとスタイル

設定画面や詳細情報、セットアップフローへユーザーを直接案内する必要がある場合は、Action ボタンを追加します。

struct FeatureTip: Tip {
    var title: Text { Text("Try the New Editor") }

    var actions: [Action] {
        Action(id: "open-editor", title: "Open Editor")
        Action(id: "learn-more", title: "Learn More")
    }
}

TipView(FeatureTip()) { action in
    switch action.id {
    case "open-editor":
        openEditor()
    case "learn-more":
        showHelp()
    default:
        break
    }
}

外観をカスタマイズする場合は、具体的なヒントインスタンスから直接値を読み取るのではなく、TipViewStyle.Configuration の値を優先してください。これにより、TipView に適用されたラベル、ハンドラー、モディファイアが保持されます。

struct CompactTipStyle: TipViewStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack(alignment: .top) {
            configuration.image?
            VStack(alignment: .leading) {
                configuration.title?
                configuration.message?
                ForEach(configuration.actions) { action in
                    Button(action: action.handler) {
                        action.label()
                    }
                }
            }
        }
        .padding()
    }
}

ヒントグループ

TipGroup は iOS 18 以降で使用できます。監視対象のグループオブジェクトがビューの更新をまたいで保持されるよう、グループは SwiftUI の状態に保存してください。TipGroup(.ordered) の計画を検討する際は、デフォルトの優先順位と順序付きシーケンスを必ず明確に区別してください。TipGroup のデフォルトは .firstAvailable であり、後続の各ヒントを、先行するすべてのヒントが無効化されるまで待機させるには TipGroup(.ordered) が必要です。

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

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

MaxDisplayDuration は表示時間に上限を設けられますが、順序付きグループの順序制御には使いません。同じグループを複数のコントロールで使用する場合は、currentTip をキャストします。

Button("Search") { openSearch() }
    .popoverTip(tips.currentTip as? SearchTip)

テスト

テスト用のオーバーライドはデバッグ/テストコードでのみ使用し、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()

組み込みの起動引数も使用できます。

  • -com.apple.TipKit.ResetDatastore 1
  • -com.apple.TipKit.ShowAllTips 1
  • -com.apple.TipKit.ShowTips TipTypeA,TipTypeB
  • -com.apple.TipKit.HideAllTips 1

テスト用オーバーライドの優先順位は、個別の表示指定、個別の非表示指定、すべて表示、すべて非表示の順です。Tips.resetDatastore() は Tips.configure(_:) より前に実行する必要があります。

よくある間違い

やってはいけないこと:ビューから TipKit を設定する

アプリの初期化中に設定してください。ビュー単位で設定すると、ヒントの表示と競合する可能性があり、データストアがすでに設定されていることを示すエラーが発生することもあります。

やってはいけないこと:iOS 18 以降の API を iOS 17 向けの方法として紹介する

TipGroup、CloudKit の同期、MaxDisplayDuration には利用可能なバージョンの制御を適用してください。グループの優先順位については、ヒントグループの標準的な判断基準に従ってください。

やってはいけないこと:重要な情報にヒントを使う

ヒントは閉じることができ、教育目的のものです。安全上の問題、エラー、データ損失、必須の手順には、アラート、確認、インライン警告、または操作をブロックする UI を使用してください。

やってはいけないこと:テスト用オーバーライドをリリースに含める

showAllTipsForTesting() などのオーバーライドは、ルールと表示頻度の制限を回避します。#if DEBUG、テストスキームの引数、または UI テスト専用の起動引数の内側に置いてください。

やってはいけないこと:不安定な再利用可能ヒント ID を使う

ヒント ID は永続化データを管理します。再利用可能なヒントの ID が予期せず変更されると、ユーザーに重複した案内や古い案内が表示されることがあります。

レビューチェックリスト

  • Tips.configure(_:) が、アプリの初期化時にチップの表示前に一度だけ実行されます。
  • Tips.resetDatastore() は、設定前かつテスト/デバッグ時にのみ実行されます。
  • iOS 18+ と iOS 26+ の TipKit API に、利用可能性のチェックまたはフォールバックの案内があります。
  • チップの文言は簡潔で、状況に即しており、実行可能で、宣伝調ではありません。
  • インライン表示とポップオーバー表示が、周囲の UI の流れに合っています。
  • ルールが意図したユーザーを対象とし、初回起動時にすべてのチップが表示されないようになっています。
  • イベント ID は安定しており、共有する場合は名前空間が設定され、寄付のペイロードは小さくなっています。
  • 再利用可能なチップでは、安定したコンテンツ由来の値で id がオーバーライドされています。
  • ユーザーが説明された操作を実行したときにチップが無効化されます。
  • TipGroup は @State に保持され、Tip Groups の優先順位に関する判断に従っています。
  • CloudKit 同期では、必要に応じて iCloud + CloudKit、Remote notifications、専用コンテナを使用しています。
  • カスタムスタイルでは configuration の値を使い、action.label() を呼び出しています。
  • テスト用のオーバーライドはデバッグ/テスト時のみ有効で、本番環境に有効な状態で出荷されることはありません。

参考資料

コメント