Communitygithub.com

Coleção de Habilidades iOS / JordanCoin

Implemente, revise ou melhore dicas e onboarding no aplicativo usando o framework TipKit da Apple. Utilize ao adicionar dicas de descoberta de funcionalidades, fluxos de onboarding, dicas contextuais, experiências de primeiro uso, coach marks, ou ao trabalhar com o protocolo Tip, TipView, popoverTip, regras de dicas, eventos de dicas ou UI de educação de funcionalidades.

O que é JordanCoin?

O TipKit é um framework da Apple para iOS 17+ que gerencia a exibição de dicas de descoberta de funcionalidades, dicas contextuais e coach marks de onboarding. Ele controla a frequência de exibição, regras de elegibilidade e persistência, garantindo que as dicas apareçam no momento certo e desapareçam quando o usuário aprender a funcionalidade. O framework suporta plataformas como SwiftUI e pode ser usado em aplicativos iOS, iPadOS, macOS, tvOS e watchOS. Seu funcionamento se baseia na configuração inicial com `Tips.configure()`, definição de dicas através do protocolo `Tip`, exibição via `TipView` ou `popoverTip`, e controle de elegibilidade por meio de regras baseadas em parâmetros e eventos. O TipKit também oferece suporte a grupos de dicas, ações personalizadas, sincronização via CloudKit e utilidades de teste para desenvolvimento.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

TipKit

Adicione dicas de descoberta de funcionalidades, dicas contextuais e coach marks de onboarding em apps iOS 17+ usando o framework TipKit da Apple. O TipKit gerencia a frequência de exibição, regras de elegibilidade e persistência para que as dicas apareçam no momento certo e desapareçam quando o usuário aprender a funcionalidade.

Conteúdo

Configuração

Chame Tips.configure() uma vez em App.init, antes que qualquer view seja renderizada. Isso inicializa o armazenamento de dados das dicas e começa a avaliação das regras. Chamá-lo depois arrisca uma condição de corrida onde as views de dica tentam exibir antes que o armazenamento esteja pronto.

import SwiftUI
import TipKit

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

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

Opções de DatastoreLocation

OpçãoCaso de Uso
.applicationDefaultLocalização padrão, sandbox do app (a maioria dos apps)
.groupContainer(identifier:))Compartilhar estado das dicas entre app e extensões
.url(_:)URL de arquivo personalizada para controle total sobre a localização do armazenamento

Sincronização com CloudKit

Sincronize o estado das dicas entre os dispositivos do usuário para que ele não veja a mesma dica em todos os dispositivos. Adicione a opção de contêiner CloudKit junto com a localização do armazenamento.

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

Definindo Dicas

Conforme uma struct ao protocolo Tip. Forneça um title no mínimo. Adicione message para detalhes de suporte e image para um ícone principal. Mantenha os títulos curtos e orientados à ação, pois a dica aparece como um pequeno callout.

import SwiftUI
import TipKit

struct FavoriteTip: Tip {
    var title: Text { Text("Fixar Seus Favoritos") }
    var message: Text? { Text("Toque no ícone de coração para salvar itens para acesso rápido.") }
    var image: Image? { Image(systemName: "heart") }
}

Propriedades: title (obrigatório), message (detalhe opcional), image (ícone principal opcional), actions (botões opcionais), rules (condições de elegibilidade opcionais), options (frequência de exibição, contagem máxima).

Ciclo de Vida: Pendente (regras insatisfeitas) -> Elegível (todas as regras passam) -> Invalidada (descartada, acionada ou removida programaticamente). Uma vez invalidada, uma dica não reaparece a menos que o armazenamento seja redefinido.

Exibindo Dicas

Dicas Inline com TipView

Incorpore uma TipView diretamente no seu layout. Ela renderiza como um cartão arredondado que aparece e desaparece com animação. Use para dicas dentro de conteúdo rolável.

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

Dicas Popover com .popoverTip()

Anexe uma dica como um popover ancorado a qualquer view. O framework desenha uma seta do popover para a âncora. Use para dicas apontando para um controle específico.

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

// Controlar direção da seta (omitir para deixar o sistema escolher)
.popoverTip(favoriteTip, arrowEdge: .bottom)

Estilo Personalizado de TipViewStyle

Crie um estilo personalizado para controlar a aparência das dicas em todo o app. Conforme a TipViewStyle e implemente 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()
    }
}

// Aplicar globalmente ou por view
TipView(favoriteTip)
    .tipViewStyle(CustomTipStyle())

Regras de Dicas

As regras controlam quando uma dica se torna elegível. Todas as regras no array rules devem passar antes que a dica seja exibida. O TipKit suporta dois tipos de regra: baseadas em parâmetros e baseadas em eventos.

Regras Baseadas em Parâmetros

Use @Parameter para rastrear o estado do app. A dica se torna elegível quando o valor do parâmetro satisfaz a condição da regra.

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

    var title: Text { Text("Fixar Seus Favoritos") }

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

// Definir o parâmetro quando o usuário chegar à lista
FavoriteTip.hasSeenList = true

Regras Baseadas em Eventos

Use Tips.Event para rastrear ações do usuário. Contribua para o evento cada vez que a ação ocorrer. A regra é acionada quando a condição de contagem ou tempo de contribuição é atendida. Isso é ideal para dicas que devem aparecer depois que o usuário realizou uma ação várias vezes sem descobrir um recurso relacionado.

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

    var title: Text { Text("Experimente a Ação Rápida") }

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

// Contribuir cada vez que o app abrir
ShortcutTip.appOpenedEvent.donate()

Combinando Várias Regras

Coloque várias regras no array. Todas devem passar (E lógico).

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

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

    var title: Text { Text("Desbloquear Modo Avançado") }

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

Opções de Frequência de Exibição

Controle com que frequência as dicas aparecem usando a propriedade options.

struct DailyTip: Tip {
    var title: Text { Text("Lembrete Diário") }

    var options: [TipOption] {
        MaxDisplayCount(3)                   // Mostrar no máximo 3 vezes no total
        IgnoresDisplayFrequency(true)        // Ignorar o limite global de frequência
    }
}

A frequência de exibição global é definida no momento da configuração:

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

Com .daily, o sistema mostra no máximo uma dica por dia em todo o app, a menos que uma dica específica defina IgnoresDisplayFrequency(true).

Ações de Dicas

Adicione botões de ação a uma dica para interação direta. Cada ação tem um id e um rótulo. Lide com a ação no manipulador de ações da view de dica.

struct FeatureTip: Tip {
    var title: Text { Text("Experimente o Novo Editor") }
    var message: Text? { Text("Adicionamos um novo modo de edição poderoso.") }

    var actions: [Action] {
        Action(id: "open-editor", title: "Abrir Editor")
        Action(id: "learn-more", title: "Saiba Mais")
    }
}

Lide com as ações na view:

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

Grupos de Dicas

Use TipGroup para coordenar múltiplas dicas dentro de uma única view. TipGroup garante que apenas uma dica do grupo seja exibida por vez, prevenindo sobrecarga de dicas. As dicas são exibidas em ordem de prioridade.

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

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

            Button("Próximo") {
                tipGroup.currentTip?.invalidate(reason: .actionPerformed)
            }
        }
    }
}

Opções de Prioridade

InicializadorComportamento
.orderedAs dicas são exibidas na ordem em que são listadas

Quando a dica atual é invalidada, a próxima dica elegível no grupo torna-se currentTip.

Controle Programático

Invalidando Dicas

Chame invalidate(reason:) quando o usuário realizar a ação descoberta ou quando a dica não for mais relevante.

let tip = FavoriteTip()
tip.invalidate(reason: .actionPerformed)
RazãoQuando Usar
.actionPerformedO usuário realizou a ação que a dica descreve
.displayCountExceededA dica atingiu seu número máximo de exibições
.tipClosedO usuário descartou explicitamente a dica

Utilitários de Teste

O TipKit fornece métodos estáticos para controlar a visibilidade das dicas durante o desenvolvimento e testes. Proteja-os com verificações #if DEBUG ou ProcessInfo para que nunca sejam executados em builds de produção.

#if DEBUG
// Mostrar todas as dicas independentemente das regras (útil durante o desenvolvimento)
Tips.showAllTipsForTesting()

// Mostrar apenas dicas específicas
Tips.showTipsForTesting([FavoriteTip.self, ShortcutTip.self])

// Ocultar todas as dicas (útil para testes de UI que não envolvem dicas)
Tips.hideAllTipsForTesting()

// Redefinir o armazenamento de dados (limpa todo o estado das dicas, invalidações e eventos)
try? Tips.resetDatastore()
#endif

Usando ProcessInfo para Esquemas de Teste

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

Passe --show-all-tips como um argumento de inicialização no esquema Xcode para builds de desenvolvimento.

Erros Comuns

NÃO FAÇA: Chamar Tips.configure() em qualquer lugar exceto App.init

Chamar Tips.configure() em um modificador onAppear ou task de uma view cria uma condição de corrida onde as views de dica tentam renderizar antes que o armazenamento esteja pronto, causando dicas ausentes ou piscando.

// ERRADO
struct ContentView: View {
    var body: some View {
        Text("Olá")
            .task { try? Tips.configure() }  // Tarde demais, views já renderizadas
    }
}

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

NÃO FAÇA: Mostrar muitas dicas ao mesmo tempo

Exibir várias dicas simultaneamente sobrecarrega os usuários e dilui o impacto de cada dica. Os usuários aprendem a ignorá-las.

// ERRADO: Três dicas visíveis ao mesmo tempo
VStack {
    TipView(tipA)
    TipView(tipB)
    TipView(tipC)
}

// CORRETO: Use TipGroup para sequenciá-las
let group = TipGroup(.ordered) { TipA(); TipB(); TipC() }
if let currentTip = group.currentTip {
    TipView(currentTip)
}

NÃO FAÇA: Esquecer de invalidar dicas após o usuário realizar a ação

Se uma dica diz "Toque na estrela para favoritar" e o usuário toca na estrela mas a dica permanece, isso corrói a confiança na UI.

// ERRADO: Dica permanece visível após o usuário agir
Button("Favoritar") { toggleFavorite() }
    .popoverTip(favoriteTip)

// CORRETO: Invalidar na ação
Button("Favoritar") {
    toggleFavorite()
    favoriteTip.invalidate(reason: .actionPerformed)
}
.popoverTip(favoriteTip)

NÃO FAÇA: Deixar dicas de teste habilitadas em produção

Tips.showAllTipsForTesting() ignora todas as regras e limites de frequência. Enviar isso em produção significa que todos os usuários veem todas as dicas imediatamente.

// ERRADO: Sempre ativo
Tips.showAllTipsForTesting()

// CORRETO: Protegido por DEBUG
#if DEBUG
Tips.showAllTipsForTesting()
#endif

NÃO FAÇA: Criar títulos de dicas muito longos

Títulos longos são truncados ou quebrados de forma estranha no pequeno callout da dica. Coloque a ação principal no título e o contexto de suporte na mensagem.

// ERRADO
var title: Text { Text("Você pode tocar no botão de coração para salvar este item na sua lista de favoritos") }

// CORRETO
var title: Text { Text("Salvar nos Favoritos") }
var message: Text? { Text("Toque no ícone de coração para manter itens para acesso rápido.") }

NÃO FAÇA: Usar dicas para informações críticas

Os usuários podem descartar dicas a qualquer momento e elas não reaparecem. Nunca coloque instruções essenciais ou informações de segurança em uma dica.

// ERRADO: Informação crítica em uma dica descartável
struct DataLossTip: Tip {
    var title: Text { Text("Alterações não salvas serão perdidas") }
}

// CORRETO: Use um alerta ou aviso inline para informações críticas
// Reserve dicas para descoberta de funcionalidades e divulgação progressiva

Checklist de Revisão

  • Tips.configure() chamado em App.init, antes que qualquer view seja renderizada
  • Cada dica tem um título claro e conciso (orientado à ação, abaixo de ~40 caracteres)
  • Dicas invalidadas quando o usuário realiza a ação descoberta
  • Regras definidas para que as dicas apareçam no momento certo (não imediatamente no primeiro lançamento para todas as dicas)
  • TipGroup usado quando múltiplas dicas existem em uma visão
  • Utilitários de teste (showAllTipsForTesting, resetDatastore) protegidos atrás de #if DEBUG
  • Sincronização CloudKit configurada se o app suportar múltiplos dispositivos
  • Frequência de exibição definida adequadamente (.daily ou .weekly para a maioria dos apps)
  • Dicas usadas apenas para descoberta de funcionalidades, não para informações críticas
  • TipViewStyle personalizado aplicado consistentemente se o estilo padrão não corresponder ao design do app
  • Ações de dicas tratadas e dica invalidada no manipulador de ações
  • Contribuições de eventos colocadas nos pontos corretos de ação do usuário
  • Garantir que tipos Tip personalizados sejam Sendable; configurar Tips em @MainActor

Referências

  • Consulte referências/tipkit-patterns.md para padrões de implementação completos incluindo estilos personalizados, regras baseadas em eventos, grupos de dicas, estratégias de teste, fluxos de onboarding e configuração de pré-visualização 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.

Habilidades Relacionadas