Desafios e Soluções no Código
Estrutura inicial do AirClipboard no macOS
O AirClipboardApp.swift define o ponto de entrada do app com @main
usando SwiftUI moderno.
Integra @NSApplicationDelegateAdaptor
para acessar recursos avançados do macOS e gerenciar o ciclo de vida da aplicação via AppDelegate.
Também define o MenuBarExtra
, o responsável pelo ícone e menu na barra de menus do sistema, permitindo que o usuário acesse as funções do AirClipboard rapidamente, sem sair do fluxo de trabalho.
Destaque:
Toda a lógica de internacionalização (idioma), preferências, e ciclo de vida é conectada aqui, integrando UI moderna com APIs legadas do macOS para o melhor dos dois mundos.
Lógica central de copiar e colar no macOS
O PasteManager.swift centraliza toda a lógica de copiar e colar do AirClipboard.
Recebe qualquer item do histórico (texto, imagem, arquivo ou grupo) e manipula a área de transferência do macOS usando NSPasteboard
, aplicando o tipo correto para cada situação.
No caso de imagens, implementa uma etapa extra: gera um arquivo temporário .png
para garantir compatibilidade ao colar no Finder, um diferencial em relação à maioria dos apps do tipo.
Destaque:
Essa centralização facilita manutenção, futuras expansões e garante que a experiência de copiar/colar funcione em qualquer contexto do sistema.
Atalho global no ciclo de vida do app
Em AppDelegate.swift, o registro do atalho global é feito durante a inicialização da aplicação, dentro do método applicationDidFinishLaunching
.
Esse padrão garante que o handler do atalho já está disponível antes de qualquer interação do usuário, permitindo acionar a interface do AirClipboard a partir de qualquer contexto do sistema, imediatamente após o app ser aberto.
O ciclo de vida é controlado por AppDelegate
com integração ao HotkeyManager
, favorecendo modularização e separação de responsabilidades na arquitetura do app.
Personalização do atalho global
O HotkeyManager.swift gerencia o registro dinâmico do atalho global. Permite que o usuário configure o atalho usando símbolos padrão do macOS (ex: ⌃ control ⌘ command V), que são convertidos para Key
e Modifiers
compatíveis com a biblioteca HotKey.
O handler (keyDownHandler
) está vinculado diretamente à ação de mostrar/ocultar a janela principal do app, garantindo resposta imediata e uma experiência consistente com a proposta de produtividade do AirClipboard.
Parser de atalhos do macOS
Este método interpreta strings de atalho no formato padrão do macOS (ex: ⌃ control ⌘ command V), identifica automaticamente quais modifiers estão presentes e extrai a tecla principal, convertendo tudo para os tipos Key
e NSEvent.ModifierFlags
esperados pela biblioteca HotKey.
Com isso, é possível aceitar qualquer combinação válida digitada pelo usuário e garantir compatibilidade e flexibilidade máxima na configuração de atalhos globais.
Lógica de histórico: deduplicação, pin e persistência
ClipboardHistory.swift gerencia o histórico de itens copiados usando ObservableObject
para integração reativa com a interface em SwiftUI.
Implementa lógica para:
- Adicionar e remover itens do histórico, eliminando duplicatas automaticamente
- Fixar/desafixar (pin) itens importantes
- Limitar o número de itens armazenados, conforme preferência do usuário
- Persistir o histórico localmente usando um storage próprio
O histórico é atualizado em tempo real na interface, com suporte a scroll automático para o item recém-adicionado ou fixado.
Inicialização: Ao criar o objeto, carrega o histórico salvo no disco e ordena por itens fixados e data.
Adicionar item: Elimina duplicatas (não fixadas), insere o novo item no topo, limita o tamanho do histórico conforme a preferência do usuário e salva tudo automaticamente.
Pin/unpin: Permite fixar ou desafixar qualquer item, reordenando o histórico e persistindo a alteração.
Componente visual do histórico: exibindo, interagindo e adaptando
ClipboardItemView.swift define o componente SwiftUI responsável por renderizar cada item do histórico do AirClipboard.
Sua arquitetura modular inclui:
- Estrutura visual dinâmica: O
body
organiza a hierarquia visual do card, usando overlays para apresentar elementos contextuais como botão de pin, timestamp, menu de ações e contagem de arquivos em grupo. Efeitos de hover são aplicados para melhorar a percepção de foco e interatividade.
contentView(for:)
: Função central que recebe o tipo de conteúdo do item e renderiza automaticamente o layout correto, seja texto (com favicon, truncamento, multilinhas), arquivo (ícone + nome), imagem (thumbnail) ou grupo de arquivos (miniaturas e contagem extra).
- Menu contextual (
menuOverlay
): Cada item traz um menu de ações rápidas, como copiar novamente, fixar/desafixar (pin/unpin), e excluir, melhorando a usabilidade e a produtividade.
- Adaptação visual: O componente aplica automaticamente o tema claro/escuro do macOS, bordas animadas em destaque, e usa estilos de botão/ícone nativos para manter o padrão visual do sistema.
- Integração com lógica global: As ações de copiar, fixar ou remover interagem diretamente com
PasteManager
e ClipboardHistory
, atualizando o estado global do app e garantindo experiência fluida e consistente.
Toda a lógica do componente é reativa, permitindo atualizações instantâneas na interface sempre que o histórico muda.
Estrutura visual dinâmica: O body
monta o card usando overlays para ações rápidas e destaca no hover, garantindo clareza e acessibilidade.
contentView(for:)
: Renderiza dinamicamente texto, arquivos, imagens ou grupos, adaptando layout, ícones e formatação automaticamente ao tipo de conteúdo.
Menu contextual: Menu integrado com ações rápidas (copiar, fixar/desafixar, remover), vinculado à lógica global do app.
Preferências e atalhos globais: centralizando personalização
PreferencesView.swift define a interface de configurações do AirClipboard usando SwiftUI, com navegação modular por seções.
Cada preferência é conectada ao AppEnvironment, permitindo que mudanças de tema, idioma ou atalho global sejam refletidas instantaneamente em todo o app.
Sidebar modular: Permite navegar facilmente entre as principais seções de configuração do app.
Views separadas: Cada área de preferência possui sua própria view, facilitando manutenção e escalabilidade do código.
Estado global: Configurações como tema, idioma e licença são controladas por AppEnvironment
e propagadas em tempo real via ObservableObject
e @AppStorage
.