Communitygithub.com

JordanCoin/ios-skills-collection

Implementa, revisa o mejora tips y onboarding dentro de la aplicación usando el framework TipKit de Apple. Úsalo para agregar tooltips de descubrimiento de funciones, flujos de onboarding, tips contextuales, experiencias de primer uso, marcadores de enseñanza o para trabajar con el protocolo Tip, TipView, popoverTip, reglas de tip, eventos de tip o UI de educación sobre funciones.

¿Qué es ios-skills-collection?

Este skill proporciona una guía completa para implementar tips de descubrimiento de funciones, sugerencias contextuales y marcadores de enseñanza en aplicaciones iOS 17+ utilizando el framework TipKit de Apple. TipKit gestiona la frecuencia de visualización, las reglas de elegibilidad y la persistencia, asegurando que los tips aparezcan en el momento adecuado y desaparezcan cuando el usuario haya aprendido la funcionalidad. El skill cubre desde la configuración inicial (llamando a `Tips.configure()` en `App.init`), la definición de tips conformando al protocolo `Tip`, hasta su visualización con `TipView` (inline) o `.popoverTip()` (popover). Incluye reglas basadas en parámetros y eventos, acciones en tips, grupos de tips con `TipGroup` para evitar sobrecarga, control programático con invalidación y utilidades de testing, y consejos sobre errores comunes. Está diseñado para desarrolladores iOS que buscan integrar educación sobre funciones de manera nativa y eficiente en sus apps.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

TipKit

Agrega tips de descubrimiento de funciones, sugerencias contextuales y marcadores de enseñanza a aplicaciones iOS 17+ usando el framework TipKit de Apple. TipKit gestiona la frecuencia de visualización, las reglas de elegibilidad y la persistencia para que los tips aparezcan en el momento adecuado y desaparezcan una vez que el usuario ha aprendido la funcionalidad.

Contenidos

Configuración

Llama a Tips.configure() una vez en App.init, antes de que se renderice cualquier vista. Esto inicializa el almacén de datos de tips y comienza la evaluación de reglas. Llamarlo más tarde corre el riesgo de una condición de carrera donde las vistas de tip intentan mostrarse antes de que el almacén esté listo.

import SwiftUI
import TipKit

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

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

Opciones de Ubicación del Almacén de Datos

OpciónCaso de Uso
.applicationDefaultUbicación predeterminada, zona de pruebas de la aplicación (la mayoría de las apps)
.groupContainer(identifier:)Compartir estado de tips entre la app y sus extensiones
.url(_:)URL de archivo personalizada para control total sobre la ubicación de almacenamiento

Sincronización con CloudKit

Sincroniza el estado de los tips entre los dispositivos de un usuario para que no vea el mismo tip en todos ellos. Añade la opción del contenedor CloudKit junto con la ubicación del almacén de datos.

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

Definición de Tips

Conforma una estructura al protocolo Tip. Proporciona un title como mínimo. Añade message para detalles complementarios e image para un icono principal. Mantén los títulos cortos y orientados a la acción porque el tip aparece como un callout compacto.

import TipKit

struct FavoriteTip: Tip {
    var title: Text { Text("Fija tus Favoritos") }
    var message: Text? { Text("Toca el icono de corazón para guardar elementos y acceder rápidamente a ellos.") }
    var image: Image? { Image(systemName: "heart") }
}

Propiedades: title (obligatorio), message (detalle opcional), image (icono principal opcional), actions (botones opcionales), rules (condiciones de elegibilidad opcionales), options (frecuencia de visualización, recuento máximo).

Ciclo de vida: Pendiente (reglas no cumplidas) -> Elegible (todas las reglas pasan) -> Invalidado (descartado, se ha actuado sobre él o eliminado programáticamente). Una vez invalidado, un tip no reaparece a menos que se reinicie el almacén de datos.

Visualización de Tips

Tips en Línea con TipView

Incrusta un TipView directamente en tu diseño. Se renderiza como una tarjeta redondeada que aparece y desaparece con animación. Úsalo para tips dentro de contenido desplazable.

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

Tips Popover con .popoverTip()

Adjunta un tip como un popover anclado a cualquier vista. El framework dibuja una flecha desde el popover hasta el ancla. Úsalo para tips que señalan un control específico.

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

// Controla la dirección de la flecha (omite para que el sistema elija)
.popoverTip(favoriteTip, arrowEdge: .bottom)

Estilo de TipView Personalizado

Crea un estilo personalizado para controlar la apariencia de los tips en toda la aplicación. Conforma a TipViewStyle e implementa 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()
    }
}

// Aplica globalmente o por vista
TipView(favoriteTip)
    .tipViewStyle(CustomTipStyle())

Reglas de Tip

Las reglas controlan cuándo un tip se vuelve elegible. Todas las reglas en el array rules deben cumplirse para que el tip se muestre. TipKit admite dos tipos de reglas: basadas en parámetros y basadas en eventos.

Reglas Basadas en Parámetros

Usa @Parameter para rastrear el estado de la aplicación. El tip se vuelve elegible cuando el valor del parámetro satisface la condición de la regla.

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

    var title: Text { Text("Fija tus Favoritos") }

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

// Establece el parámetro cuando el usuario llega a la lista
FavoriteTip.hasSeenList = true

Reglas Basadas en Eventos

Usa Tips.Event para rastrear acciones del usuario. Dona al evento cada vez que ocurre la acción. La regla se activa cuando se cumple la condición de recuento o tiempo de donación. Esto es ideal para tips que deberían aparecer después de que el usuario haya realizado una acción varias veces sin descubrir una función relacionada.

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

    var title: Text { Text("Prueba la Acción Rápida") }

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

// Dona cada vez que se abre la aplicación
ShortcutTip.appOpenedEvent.donate()

Combinación de Múltiples Reglas

Coloca múltiples reglas en el array. Todas deben cumplirse (Y lógico).

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

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

    var title: Text { Text("Desbloquea el Modo Avanzado") }

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

Opciones de Frecuencia de Visualización

Controla la frecuencia con la que aparecen los tips usando la propiedad options.

struct DailyTip: Tip {
    var title: Text { Text("Recordatorio Diario") }

    var options: [TipOption] {
        MaxDisplayCount(3)                   // Muestra como máximo 3 veces en total
        IgnoresDisplayFrequency(true)        // Omite el límite de frecuencia global
    }
}

La frecuencia de visualización global se configura al inicializar:

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

Con .daily, el sistema muestra como máximo un tip al día en toda la aplicación, a menos que un tip específico establezca IgnoresDisplayFrequency(true).

Acciones de Tip

Añade botones de acción a un tip para interacción directa. Cada acción tiene un id y una etiqueta. Maneja la acción en el controlador de acciones de la vista de tip.

struct FeatureTip: Tip {
    var title: Text { Text("Prueba el Nuevo Editor") }
    var message: Text? { Text("Hemos añadido un potente modo de edición.") }

    var actions: [Action] {
        Action(id: "open-editor", title: "Abrir Editor")
        Action(id: "learn-more", title: "Más Información")
    }
}

Maneja las acciones en la vista:

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

Grupos de Tip

Usa TipGroup para coordinar múltiples tips dentro de una sola vista. TipGroup asegura que solo un tip del grupo se muestre a la vez, evitando la sobrecarga de tips. Los tips se muestran en orden de prioridad.

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

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

            Button("Siguiente") {
                tipGroup.currentTip?.invalidate(reason: .actionPerformed)
            }
        }
    }
}

Opciones de Prioridad

InicializadorComportamiento
.orderedLos tips se muestran en el orden en que se enumeran

Cuando el tip actual se invalida, el siguiente tip elegible en el grupo se convierte en currentTip.

Control Programático

Invalidación de Tips

Llama a invalidate(reason:) cuando el usuario realiza la acción descubierta o cuando el tip ya no es relevante.

let tip = FavoriteTip()
tip.invalidate(reason: .actionPerformed)
RazónCuándo Usarla
.actionPerformedEl usuario realizó la acción que describe el tip
.displayCountExceededEl tip alcanzó su recuento máximo de visualizaciones
.tipClosedEl usuario descartó explícitamente el tip

Utilidades de Prueba

TipKit proporciona métodos estáticos para controlar la visibilidad de los tips durante el desarrollo y las pruebas. Protéjelos con #if DEBUG o comprobaciones de ProcessInfo para que nunca se ejecuten en las versiones de producción.

#if DEBUG
// Muestra todos los tips independientemente de las reglas (útil durante el desarrollo)
Tips.showAllTipsForTesting()

// Muestra solo tips específicos
Tips.showTipsForTesting([FavoriteTip.self, ShortcutTip.self])

// Oculta todos los tips (útil para pruebas de UI que no involucran tips)
Tips.hideAllTipsForTesting()

// Reinicia el almacén de datos (borra todo el estado de tips, invalidaciones y eventos)
try? Tips.resetDatastore()
#endif

Uso de ProcessInfo para Esquemas de Prueba

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

Pasa --show-all-tips como argumento de lanzamiento en el esquema de Xcode para las versiones de desarrollo.

Errores Comunes

NO: Llamar a Tips.configure() en cualquier lugar que no sea App.init

Llamar a Tips.configure() en un modificador onAppear o task de una vista crea una condición de carrera donde las vistas de tip intentan renderizarse antes de que el almacén de datos esté listo, causando tips faltantes o parpadeantes.

// INCORRECTO
struct ContentView: View {
    var body: some View {
        Text("Hola")
            .task { try? Tips.configure() }  // Demasiado tarde, las vistas ya se renderizaron
    }
}

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

NO: Mostrar demasiados tips a la vez

Mostrar múltiples tips simultáneamente abruma a los usuarios y diluye el impacto de cada tip. Los usuarios aprenden a ignorarlos.

// INCORRECTO: Tres tips visibles al mismo tiempo
VStack {
    TipView(tipA)
    TipView(tipB)
    TipView(tipC)
}

// CORRECTO: Usa TipGroup para secuenciarlos
let group = TipGroup(.ordered) { TipA(); TipB(); TipC() }
if let currentTip = group.currentTip {
    TipView(currentTip)
}

NO: Olvidar invalidar los tips después de que el usuario realice la acción

Si un tip dice "Toca la estrella para marcar como favorito" y el usuario toca la estrella pero el tip permanece, se erosiona la confianza en la interfaz de usuario.

// INCORRECTO: El tip permanece visible después de que el usuario actúa
Button("Favorito") { toggleFavorite() }
    .popoverTip(favoriteTip)

// CORRECTO: Invalidar al actuar
Button("Favorito") {
    toggleFavorite()
    favoriteTip.invalidate(reason: .actionPerformed)
}
.popoverTip(favoriteTip)

NO: Dejar los tips de prueba habilitados en producción

Tips.showAllTipsForTesting() omite todas las reglas y límites de frecuencia. Enviar esto a producción significa que todos los usuarios verán cada tip inmediatamente.

// INCORRECTO: Siempre activo
Tips.showAllTipsForTesting()

// CORRECTO: Protegido detrás de DEBUG
#if DEBUG
Tips.showAllTipsForTesting()
#endif

NO: Hacer los títulos de los tips demasiado largos

Los títulos largos se truncan o se ajustan de manera incómoda en el callout compacto del tip. Pon la acción clave en el título y el contexto complementario en el mensaje.

// INCORRECTO
var title: Text { Text("Puedes tocar el botón de corazón para guardar este elemento en tu lista de favoritos") }

// CORRECTO
var title: Text { Text("Guardar en Favoritos") }
var message: Text? { Text("Toca el icono de corazón para mantener los elementos accesibles rápidamente.") }

NO: Usar tips para información crítica

Los usuarios pueden descartar los tips en cualquier momento y no reaparecen. Nunca pongas instrucciones esenciales o información de seguridad en un tip.

// INCORRECTO: Información crítica en un tip descartable
struct DataLossTip: Tip {
    var title: Text { Text("Los cambios no guardados se perderán") }
}

// CORRECTO: Usa una alerta o advertencia en línea para información crítica
// Reserva los tips para descubrimiento de funciones y revelación progresiva

Lista de Verificación para Revisión

  • Tips.configure() llamado en App.init, antes de que se renderice cualquier vista
  • Cada tip tiene un título claro y conciso (orientado a la acción, menos de ~40 caracteres)
  • Los tips se invalidan cuando el usuario realiza la acción descubierta
  • Reglas configuradas para que los tips aparezcan en el momento adecuado (no inmediatamente en el primer lanzamiento para todos los tips)
  • Se usa TipGroup cuando hay múltiples tips en una sola vista
  • Las utilidades de prueba (showAllTipsForTesting, resetDatastore) están protegidas detrás de #if DEBUG
  • Sincronización con CloudKit configurada si la aplicación soporta múltiples dispositivos
  • Frecuencia de visualización establecida adecuadamente (.daily o .weekly para la mayoría de las apps)
  • Tips usados solo para descubrimiento de funciones, no para información crítica
  • Se aplica TipViewStyle personalizado de manera consistente si el estilo predeterminado no coincide con el diseño de la aplicación
  • Las acciones del tip se manejan y el tip se invalida en el controlador de acciones
  • Las donaciones de eventos se colocan en los puntos correctos de la acción del usuario
  • Asegúrate de que los tipos de Tip personalizados sean Sendable; configura Tips en @MainActor

Referencias

  • Consulta references/tipkit-patterns.md para patrones de implementación completos, incluidos estilos personalizados, reglas basadas en eventos, grupos de tips, estrategias de prueba, flujos de onboarding y configuración de previsualización en 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.

Skills relacionados