TipKit es el framework de Apple para mostrar tips contextuales de onboarding en apps iOS 17 y posteriores. La ventaja principal sobre gestionar manualmente UserDefaults o contadores de uso es que TipKit se encarga del almacenamiento, la frecuencia de visualización y la evaluación de reglas de forma declarativa.
Definir un tip: el protocolo Tip
Un tip es una estructura que conforma el protocolo Tip. El requisito mínimo es un title y, opcionalmente, un message e image:
import TipKit
struct TipFiltros: Tip {
var title: Text {
Text("Filtra tus resultados")
}
var message: Text? {
Text("Usa el botón de filtro para mostrar solo las tareas del día de hoy.")
}
var image: Image? {
Image(systemName: "line.3.horizontal.decrease.circle")
}
}
Mostrar tips con TipView y popoverTip
Hay dos formas de mostrar un tip: inline con TipView o como popover con el modificador .popoverTip:
struct ListaTareas: View {
private let tipFiltros = TipFiltros()
var body: some View {
VStack {
// Tip inline: ocupa espacio en la jerarquía de vistas
TipView(tipFiltros, arrowEdge: .bottom)
List(tareas) { tarea in
TareaRow(tarea: tarea)
}
}
.toolbar {
ToolbarItem {
Button(action: mostrarFiltros) {
Image(systemName: "line.3.horizontal.decrease.circle")
}
// Tip como popover sobre el botón
.popoverTip(tipFiltros, arrowEdge: .top)
}
}
}
}
Reglas condicionales: Tips Rules
Los tips se muestran solo cuando se cumplen todas sus reglas. Existen dos tipos: reglas basadas en parámetros (@Parameter) y reglas basadas en eventos (Event):
struct TipExportacion: Tip {
// Parámetro tipado almacenado automáticamente por TipKit
@Parameter
static var usuarioHaCreado3Tareas: Bool = false
var title: Text { Text("Exporta tus tareas") }
var message: Text? { Text("Ya tienes suficientes tareas para exportarlas a PDF.") }
// El tip solo se muestra cuando la regla es verdadera
var rules: [Rule] {
#Rule(Self.$usuarioHaCreado3Tareas) { $0 == true }
}
}
// En la app, cuando corresponda:
TipExportacion.usuarioHaCreado3Tareas = (TareaManager.shared.tareas.count >= 3)
Eventos acumulados: mostrar un tip tras N usos
Los eventos permiten contar acciones del usuario y mostrar el tip después de un umbral:
struct TipAccesoDirecto: Tip {
// Evento que cuenta las veces que el usuario navega manualmente
static let navegacionManual = Event(id: "navegacion.manual")
var title: Text { Text("Usa el acceso directo") }
var message: Text? { Text("Sabes que puedes deslizar para cambiar de sección, ¿verdad?") }
var rules: [Rule] {
// Mostrar tras 5 navegaciones manuales
#Rule(Self.navegacionManual) { $0.donations.count >= 5 }
}
}
// Cada vez que el usuario navega:
Task {
await TipAccesoDirecto.navegacionManual.donate()
}
Frecuencia de visualización
Por defecto TipKit muestra un tip solo una vez. Se puede cambiar la frecuencia global al configurar TipKit:
@main
struct MiApp: App {
init() {
// Configurar TipKit al iniciar la app
try? Tips.configure([
.displayFrequency(.daily), // Un tip por día como máximo
.datastoreLocation(.applicationDefault)
])
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Las opciones de frecuencia disponibles son .immediate (sin límite), .hourly, .daily, .weekly y .monthly. La frecuencia afecta a todos los tips de la app, no a uno en concreto.
El antipatrón de resetDatastore en producción
Durante el desarrollo es habitual llamar a Tips.resetDatastore() para ver los tips de nuevo. Este método borra todos los datos de TipKit, incluyendo contadores de eventos y valores de parámetros. Nunca se debe llamar en el código de producción:
#if DEBUG
// Solo en desarrollo: resetear para probar tips ya vistos
try? Tips.resetDatastore()
#endif
try? Tips.configure([
.displayFrequency(.immediate) // En DEBUG, mostrar siempre
])
Invalidar un tip manualmente
Para marcar un tip como completado desde el código (por ejemplo, cuando el usuario ya realizó la acción que el tip sugería):
let tip = TipFiltros()
tip.invalidate(reason: .actionPerformed)
TipKit no mostrará ese tip de nuevo. Los motivos disponibles son .actionPerformed, .tipClosed y .displayCountExceeded.
Resumen
TipKit simplifica el onboarding contextual en iOS 17: define el tip con Tip, muéstralo con TipView o .popoverTip, controla cuándo aparece con reglas basadas en @Parameter o en eventos acumulados con Event.donate(), y ajusta la frecuencia global al configurar TipKit. El sistema gestiona automáticamente el almacenamiento y la deduplicación, eliminando la necesidad de gestionar manualmente los estados de onboarding.
