← Todos os Posts

A Mac App Store não deixa gerenciadores de janelas existirem. Publiquei um mesmo assim.

Um engenheiro do Developer Technical Support da Apple me disse que meu aplicativo era impossível: “Não é possível usar as APIs de Accessibility a partir de um aplicativo em sandbox… seu único caminho adiante é distribuir o aplicativo diretamente.”1 O aplicativo era um organizador de janelas em mosaico, a API de Accessibility é a base sobre a qual todo gerenciador de janelas é construído, e todo aplicativo da Mac App Store roda dentro do sandbox que a proíbe. Na semana passada, o 941 Tiles entrou na Mac App Store mesmo assim: um clique na barra de menus e todas as janelas espalhadas de um aplicativo se encaixam em uma grade uniforme. A regra do sandbox nunca cedeu. O truque foi perceber que o macOS já contém um movedor de janelas privilegiado, com a bênção total da Apple, e que ele aceita JSON.

TL;DR

  • O App Sandbox bloqueia categoricamente as chamadas de Accessibility entre aplicativos, nenhum entitlement destrava isso, e todo aplicativo da Mac App Store precisa estar em sandbox. A categoria de gerenciadores de janelas está fechada na prática para os recém-chegados desde junho de 2012; os organizadores que ainda estão na loja são anteriores a essa exigência e rodam fora do sandbox para sempre.134
  • A porta autorizada: um aplicativo em sandbox pode acionar o motor Shortcuts da Apple por meio de Apple events, e o Shortcuts traz as ações Find Windows / Move Window / Resize Window, que movem as janelas de outros aplicativos com os privilégios da própria Apple.25 Meu aplicativo calcula para onde cada janela deve ir; um Shortcut complementar faz o movimento. Delegação, não permissão.
  • A engenharia que tornou isso real: gerar por código o arquivo .shortcut complementar como um plist binário de WFWorkflowActions, um contrato de payload JSON com os frames indexado pela ordem de profundidade das janelas, proteções contra janelas fantasmas e estouro de índice, e uma base de código com dois motores, que usa Accessibility direto nos builds de Developer ID e delegação ao Shortcuts nos builds da loja.
  • Primeira execução bem-sucedida na máquina em que cinco builds da loja tinham falhado: quatro janelas de terminal espalhadas foram para os frames exatos dos quadrantes em 0,6 segundo.
  • Crédito a quem merece: o mecanismo já está comprovado na prática pelo snApp (código aberto, 2023) e pelo Align (2024). Foi a leitura do código-fonte do snApp que transformou o “impossível” em um plano de desenvolvimento.2

A parede

A primeira versão do 941 Tiles era um organizador de janelas clássico na barra de menus. O macOS tem um sistema Accessibility que, com a permissão do usuário, deixa um aplicativo enumerar e mover as janelas de outro (AXUIElementSetAttributeValue nos atributos de posição e tamanho da janela). Construído sobre isso, o aplicativo funcionou sem uma única falha por semanas de desenvolvimento. Eu já tinha preparado a página inteira no App Store Connect: metadados em 17 idiomas, capturas de tela, preço, 21 builds enviados.

Então um build do TestFlight foi para um segundo Mac. Permissão de Accessibility concedida, o aplicativo informando que o acesso estava ativo, e ele conseguia ler zero janelas.

Não era um bug. Todo aplicativo da Mac App Store roda dentro do App Sandbox da Apple, uma regra de contenção segundo a qual um aplicativo só pode tocar nas próprias coisas, justamente para que não consiga espionar nem interferir no resto do seu Mac.4 Estender a mão até as janelas de outro aplicativo é exatamente o que o sandbox existe para impedir. Os builds de desenvolvimento rodam fora do sandbox, e é por isso que tudo funcionou por semanas; o build da loja aplicou a regra que eu vinha ignorando. Nenhuma caixa de diálogo de permissão, nenhum entitlement, nenhuma configuração destrava isso. Quando perguntei diretamente à Apple, a resposta do DTS foi aquela que abre este texto: distribuir fora da loja.1

Uma categoria fechada desde 2012

O que torna essa parede interessante é quem está do outro lado dela. Magnet, Moom, BetterSnapTool: ferramentas de janelas que você pode comprar hoje mesmo na Mac App Store. As páginas deles são anteriores à exigência de sandbox de junho de 2012, então rodam fora do sandbox sob uma cláusula de direito adquirido que não passa para mais ninguém. Todo gerenciador de janelas nascido depois de 2012 (Rectangle, Swish, Loop, BetterTouchTool, Amethyst) é distribuído fora da loja. A Apple também fez valer a linha no sentido inverso: o PopClip, quase 13 anos na loja, saiu em março de 2024 depois que a revisão informou ao desenvolvedor que uma nova funcionalidade baseada em Accessibility não poderia ser publicada em uma atualização em sandbox.3

Quero ser direto sobre a minha leitura: a regra do sandbox é razoável. Um aplicativo capaz de ler e mover silenciosamente cada janela da sua tela é um aplicativo capaz de vigiar você, e “confie em mim” não é um modelo de segurança. A promessa da loja é que nada do que você instala ali pode fazer isso. A injustiça é estreita e específica: quem já estava mantém uma capacidade que os recém-chegados não conseguem conquistar com nenhuma quantidade de consentimento do usuário. A categoria não foi proibida; foi fossilizada.

A virada

O veredito do DTS tinha uma falha: a loja já continha contraexemplos. O Align (2024) e o snApp (2023) organizam janelas em mosaico, os dois vivem na Mac App Store e os dois nasceram muito depois de 2012. O snApp é de código aberto, então o mecanismo não é um segredo: é um repositório.2

O aplicativo em sandbox nunca toca nas janelas de outro aplicativo. Ele aciona o Shortcuts, o próprio aplicativo de automação da Apple, por meio de dois entitlements declarados:

<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.temporary-exception.apple-events</key>
<array>
  <string>com.apple.shortcuts.events</string>
</array>

Desde o macOS 12, o Shortcuts traz ações chamadas Find Windows, Move Window e Resize Window.5 O Shortcuts tem permissão para mover janelas porque é da Apple. Então o aplicativo calcula para onde cada janela deve ir, entrega esse plano a um Shortcut complementar instalado pelo usuário, e o motor da própria Apple faz o movimento. O sandbox nunca é violado, porque o processo em sandbox nunca executa o ato privilegiado. Delegação, não permissão.

Há algo quase arquitetônico no formato disso: a fronteira de segurança fica exatamente onde a Apple a desenhou, e a capacidade atravessa essa fronteira passando pelo único processo do sistema que legitimamente a detém.

A construção

Conhecer o mecanismo e publicar o produto são semanas diferentes. As peças que tornaram isso digno de produção:

Gerar o Shortcut por código. O usuário não deveria montar à mão um fluxo de 12 ações. O arquivo .shortcut complementar é um plist binário de WFWorkflowActions que o aplicativo gera e assina em tempo de build com shortcuts sign -m anyone, de modo que a instalação é um clique dentro do aplicativo Shortcuts. O contrato em tempo de execução é um payload JSON enviado pelo shortcut.run(withInput:) do ScriptingBridge:

{"app": "Ghostty", "frames": [{"x": 0, "y": 38, "w": 1280, "h": 771}, ...]}

A identidade da janela é a ordem de profundidade. A ação Find Windows do Shortcut indexa as janelas da frente para trás, e o CGWindowList as enumera na mesma ordem. Assim, a posição i do payload carrega simplesmente o frame da janela i, e ao distribuir as células por ordem de posição a grade reproduz o princípio “as janelas ficam perto de onde estavam”. Nenhum identificador de janela atravessa a fronteira, porque nenhum existe do outro lado.

Proteções. Um limitador Items in Range delimitado por Count impede que um payload desatualizado indexe além da lista real de janelas. Um filtro de tamanho mínimo (120×80) exclui os painéis fantasmas estacionados que o CGWindowList reporta e que a antiga verificação de papel do Accessibility costumava esconder. E um fato que me surpreendeu: listar janelas não exige permissão nenhuma; o CGWindowList é legal dentro do sandbox. Só mover sempre foi proibido.

Uma base de código, dois motores. O aplicativo detecta o sandbox na inicialização por meio da variável de ambiente APP_SANDBOX_CONTAINER_ID. Os builds da loja organizam as janelas pelo Shortcuts; os builds de Developer ID e de desenvolvimento usam o caminho direto do Accessibility. Mesma interface, mesma matemática, movedor diferente.

Modo de observação sem notificações. O recurso opcional de reorganização automática normalmente assinaria as notificações de janelas do Accessibility, que o sandbox também remove. Ele sobrevive como polling do servidor de janelas: comparações baratas de CGWindowList e reorganização a cada mudança.

O custo, dito com honestidade

O usuário paga um imposto de configuração único: instalar o Shortcut complementar (um clique) e conceder uma caixa de diálogo de consentimento de automação (o aviso padrão “941 Tiles quer controlar o Shortcuts”, localizado nos 17 idiomas via InfoPlist.xcstrings). Depois disso, a experiência é idêntica à de um organizador com API direta: clique no ícone de grade, clique em um aplicativo, as janelas pousam. A execução do Shortcuts é invisível e leva bem menos de um segundo.

Não vou fingir que esse imposto é nada; é uma tela a mais do que o Magnet mostra. É também o preço integral de existir dentro do sandbox, em vez de pedir que os usuários driblem o Gatekeeper por um download notarizado no qual eles ainda não têm motivo para confiar.

O resultado

O mesmo Mac Intel em que cinco builds da loja tinham falhado: instalação, configuração do Shortcut, consentimento, e quatro janelas espalhadas do Ghostty encaixadas nos frames exatos dos quadrantes em 0,6 segundo já na primeira execução. Depois veio a rodada de internacionalização (String Catalogs, regras de plural do CLDR incluindo todas as seis categorias do árabe), a página localizada e o envio. Após uma rejeição na revisão e uma nova revisão de cerca de 30 minutos, o 941 Tiles está no ar: 4,99 USD de pagamento único, macOS 15+, 17 idiomas, zero coleta de dados, nenhum acesso à rede.

O que se generaliza

O padrão é maior do que organizar janelas. Quando uma plataforma fecha uma capacidade atrás de uma fronteira, a resposta raramente é brigar com a fronteira; é inventariar quais processos nativos já detêm essa capacidade e expõem uma interface autorizada para ela. No macOS, esse inventário aponta cada vez mais para um lugar só: o Shortcuts, que silenciosamente virou o intermediário de automação privilegiada do sistema, alcançável de dentro do sandbox por design. Meu guia do Shortcuts cobre essa superfície em profundidade, e o estudo de caso do 941 Tiles percorre esta mesma história com os diagramas de arquitetura.

O sandbox fez o trabalho dele. O aplicativo foi publicado mesmo assim. As duas frases são verdadeiras, e a porta entre elas era da própria Apple.

Perguntas frequentes

Um aplicativo Mac em sandbox pode usar as APIs de Accessibility?

Não. O App Sandbox bloqueia categoricamente as chamadas de Accessibility entre aplicativos, e nenhum entitlement, caixa de diálogo de permissão ou configuração destrava isso. A resposta que o próprio DTS da Apple me deu foi: “Não é possível usar as APIs de Accessibility a partir de um aplicativo em sandbox… seu único caminho adiante é distribuir o aplicativo diretamente.” Os builds de desenvolvimento rodam fora do sandbox, e é por isso que um organizador pode funcionar por semanas e depois ler zero janelas em um build da loja.14

Como um gerenciador de janelas pode ser publicado na Mac App Store?

Delegando o ato privilegiado. Um aplicativo em sandbox pode acionar o motor Shortcuts da Apple por meio de Apple events, e o Shortcuts traz as ações Find Windows, Move Window e Resize Window desde o macOS 12. Meu aplicativo calcula para onde cada janela deve ir e entrega esse plano a um Shortcut complementar instalado pelo usuário, que faz o movimento com os privilégios da própria Apple. O sandbox nunca é violado, porque o processo em sandbox nunca executa o ato privilegiado.25

Por que Magnet e Moom estão na loja se gerenciadores de janelas não podem estar?

As páginas deles são anteriores à exigência de sandbox de junho de 2012, então rodam fora do sandbox sob uma cláusula de direito adquirido que não passa para mais ninguém. Todo gerenciador de janelas nascido depois de 2012, incluindo Rectangle, Swish, Loop, BetterTouchTool e Amethyst, é distribuído fora da loja. O PopClip saiu em março de 2024 depois que a revisão informou ao desenvolvedor que uma nova funcionalidade de Accessibility não poderia ser publicada em uma atualização em sandbox. A categoria não foi proibida; foi fossilizada.34

Quanto a delegação ao Shortcuts custa para o usuário?

Um imposto de configuração único: instalar o Shortcut complementar com um clique e depois conceder uma caixa de diálogo de consentimento de automação. Daí em diante a experiência é idêntica à de um organizador com API direta: clique no ícone de grade, clique em um aplicativo, as janelas pousam, e a execução do Shortcuts fica invisível, em bem menos de um segundo. É uma tela a mais do que o Magnet mostra, e é o preço integral de existir dentro do sandbox.

Como uma única base de código atende builds em sandbox e builds diretos?

O aplicativo detecta o sandbox na inicialização por meio da variável de ambiente APP_SANDBOX_CONTAINER_ID. Os builds da loja organizam as janelas pelo Shortcuts; os builds de Developer ID e de desenvolvimento usam o caminho direto do Accessibility. Mesma interface, mesma matemática, movedor diferente. O modo de observação também se adapta: o sandbox remove as notificações de janelas do Accessibility, então a reorganização automática opcional sobrevive como um polling barato de CGWindowList que compara e reorganiza a cada mudança.

Referências


  1. Apple Developer Forums, tópico 805556 (outubro de 2025), resposta do DTS da Apple: “Não é possível usar as APIs de Accessibility a partir de um aplicativo em sandbox… seu único caminho adiante é distribuir o aplicativo diretamente, assinando com Developer ID.” ↩↩↩↩

  2. Om Chachad, snApp no GitHub (2023), o organizador de janelas de código aberto da Mac App Store cujo código-fonte documenta o mecanismo de delegação ao Shortcuts: os entitlements de alvo de scripting com.apple.security.automation.apple-events mais um Shortcut complementar montado com as ações de janela da Apple. ↩↩↩↩

  3. Michael Tsai, “PopClip Leaving the Mac App Store” (21 de março de 2024), sobre a revisão da Apple exigindo a remoção de uma nova funcionalidade baseada em Accessibility de uma atualização em sandbox, após quase 13 anos na loja. ↩↩↩

  4. Apple, documentação do App Sandbox, o modelo de contenção que todos os aplicativos da Mac App Store são obrigados a adotar desde junho de 2012. ↩↩↩↩

  5. Apple, Manual do Usuário do Shortcuts para Mac, que documenta as ações Find Windows, Move Window e Resize Window disponíveis desde o macOS 12. ↩↩↩

Artigos relacionados

The Shader Gallery That Lied: Debugging 216 WebGL Presets

A user said the shader playground looked broken. Pixel-readback testing found 30 dead presets, 11 that never compiled, a…

13 min de leitura

Recriando o MacPaint no navegador: o código-fonte de 1984 é a especificação

Reconstruí o MacPaint 1.3 no navegador conferindo cada ferramenta contra o código Pascal de Bill Atkinson. O que uma rec…

12 min de leitura

Filosofia de engenharia: Joanna Rutkowska

Joanna Rutkowska criou o Qubes OS sobre uma paranoia razoável: software sem bugs não existe, então presuma o comprometim…

20 min de leitura