Outro canal de distribuição — a Mac App Store

A série anterior cobriu a configuração inicial para distribuir um app macOS diretamente com um Developer ID. Uma vez que você tenha certificados, notarização, atualizações automáticas com Sparkle e um feed de atualizações hospedado, pode permitir que os usuários baixem um arquivo .dmg diretamente sem passar pela App Store.

Esta série cobre a configuração inicial para colocar esse mesmo app na Mac App Store (MAS) também. Os dois métodos de distribuição não são uma escolha mutuamente exclusiva. Você pode operar um único app pelos canais de distribuição direta e da App Store ao mesmo tempo. A App Store tem a Apple gerenciando pagamentos, reembolsos e visibilidade nas buscas em seu nome, e traz maior confiança dos usuários, então operá-la junto com a distribuição direta é uma escolha comum.

Esta série pressupõe que seu app já está sendo distribuído diretamente com um Developer ID e já tem as atualizações automáticas do Sparkle integradas. Essa configuração foi coberta a partir da Parte 1 da série “Distribuindo um app macOS por conta própria”. Se você ainda não leu, recomendo passar por ela primeiro.

Por que o MAS precisa de “outro target”

Um app distribuído diretamente inclui atualizações automáticas do Sparkle — o recurso que permite ao app verificar um feed de atualizações por conta própria, baixar uma nova versão e se substituir.

Mas as regras de revisão da Mac App Store proíbem esse comportamento. Um app na App Store não deve baixar código de fora para se atualizar; as atualizações devem acontecer apenas pela App Store. Em outras palavras, um build destinado ao MAS não deve conter o Sparkle.

O build para distribuição direta, no entanto, deve ter o Sparkle. Um único artefato de build não pode satisfazer ambos os requisitos ao mesmo tempo. Então a solução é esta:

Uma base de código, dois targets de build.

CanalTarget de buildSparkleDistribuído via
Developer IDFocusTimerIncluídoDistribuição direta (.dmg)
Mac App StoreFocusTimer MASExcluídoApp Store

Você compartilha uma única base de código-fonte, mas a divide em dois targets de build, vinculando o Sparkle apenas em um deles. Esta série cobre a configuração inicial para criar esse segundo target (FocusTimer MAS).

O que esta série constrói

Ao longo de três partes, você vai configurar o seguinte.

  • (Parte 1, este artigo) Registro de um Bundle ID exclusivo para MAS + duplicação do target de build no Xcode
  • (Parte 2) Os arquivos de configuração (entitlements e Info.plist) e a ramificação de código que separam os dois canais
  • (Parte 3) Um ExportOptions-MAS.plist para upload + registro no App Store Connect + verificação do build

Ao final da série, você deverá ter:

  • Um Bundle ID exclusivo para MAS registrado no Apple Developer Portal
  • Um target de build FocusTimer MAS com a dependência do Sparkle removida
  • Um arquivo de entitlements e um arquivo Info.plist exclusivos para MAS
  • Código ramificado com #if canImport(Sparkle)
  • Um ExportOptions-MAS.plist para upload na App Store

App de exemplo — FocusTimer

Como na série anterior, usaremos um app macOS fictício, FocusTimer (um app de temporizador simples para gerenciar sessões de foco), como exemplo. FocusTimer, o identificador de bundle com.example.FocusTimer, o Team ID ABCDE12345 e assim por diante são todos valores de exemplo. Na prática, substitua-os pelo seu próprio app e pelas informações da sua conta.

Este artigo foi escrito para o Xcode 26.

Passo 1 — Registrar um Bundle ID exclusivo para MAS

Por que um Bundle ID separado

Se o Bundle ID do canal de distribuição direta for com.example.FocusTimer, o canal MAS usa um Bundle ID diferente.

CanalBundle ID
Developer IDcom.example.FocusTimer
Mac App Storecom.example.FocusTimer.mas

Você acrescenta .mas ao Bundle ID existente. Por que se dar ao trabalho de separá-los?

  • Ambos os canais podem coexistir em um Mac — Se os Bundle IDs forem os mesmos, o macOS trata os dois apps como o mesmo app e eles entram em conflito. Se forem diferentes, você pode instalar o build de distribuição direta e o build da App Store na mesma máquina ao mesmo tempo (útil para desenvolvimento e testes).
  • Domínios UserDefaults separados — O armazenamento de configurações é particionado por Bundle ID, portanto as configurações dos dois canais não se misturam.
  • Evitar conflitos no Launch Services — Previne confusão quando o macOS decide qual app enviar uma solicitação como “abrir este app”.

Mantenha o Bundle ID do canal de distribuição direta inalterado. É o identificador para seus usuários existentes, portanto não deve ser alterado. Você está registrando mais um ID novo para o MAS.

Procedimento de registro

  1. Faça login em developer.apple.com/account (com sua conta do Apple Developer Program)
  2. Certificates, Identifiers & ProfilesIdentifiers no menu à esquerda → o botão +
  3. Selecione App IDsContinue → escolha o tipo AppContinue
  4. Description: Insira um nome facilmente reconhecível (ex.: FocusTimer MAS)
  5. Bundle ID:
    • Selecione Explicit
    • Insira: com.example.FocusTimer.mas
  6. Capabilities: Desative todos os recursos que o app não usa. Se você não usa iCloud, notificações push, compras dentro do app, Sign in with Apple e assim por diante, não precisa ativar nada. (O App Sandbox é tratado pelos entitlements no lado do Xcode, portanto não há necessidade de ativá-lo aqui.)
  7. ContinueRegister

Verificação

Se a lista de Identifiers mostrar uma entrada para FocusTimer MAS — com.example.FocusTimer.mas, o registro está completo.

Passo 2 — Duplicar o target de build no Xcode

Agora você vai criar um target de build FocusTimer MAS no projeto Xcode. A maneira mais rápida é duplicar o target existente.

2-1. Duplicar o target

  1. Abra FocusTimer.xcodeproj no Xcode
  2. Clique no ícone do projeto (azul) no topo do Project navigator (painel esquerdo)
  3. Na lista TARGETS na área de edição central, clique com o botão direito em FocusTimerDuplicate
  4. Quando a caixa de diálogo aparecer, selecione Duplicate Only
  5. O novo target é criado como FocusTimer copy. Dê um duplo clique no nome e altere-o para FocusTimer MAS
  6. Se o prompt “Add a new Scheme?” aparecer, escolha Activate (ou adicione-o depois em Manage Schemes)

2-2. Alterar o Bundle ID imediatamente

A primeira coisa a fazer logo após a duplicação é trocar o Bundle ID. Se você deixar como está, os dois targets terão o mesmo ID.

Altere TARGETS → FocusTimer MAS → General → Identity → Bundle Identifier para o valor que você registrou no Passo 1.

com.example.FocusTimer.mas

2-3. Uma armadilha importante — o “trabalho de limpeza” que a duplicação deixa para trás

Esta é a parte mais complicada deste artigo. O Duplicate Target do Xcode opera com “padrões seguros”, mas essa própria segurança deixa para trás um trabalho de limpeza que requer atenção manual. Logo após a duplicação, você precisa verificar as quatro coisas a seguir.

① Fazer o novo target incluir os arquivos de código-fonte

Por segurança, a Duplicação exclui automaticamente todos os arquivos de código-fonte do novo target. Se deixado assim, o target FocusTimer MAS é um shell vazio — compilá-lo produz um build sem código. Você precisa corrigir as associações de target para que o novo target inclua novamente os arquivos de código-fonte existentes.

② Remover a dependência do Sparkle do target MAS

A Duplicação copia as dependências do Swift Package do original literalmente. Como resultado, o Sparkle também vai para o target FocusTimer MAS. O ponto de partida desta série foi “o build MAS não deve ter Sparkle”, portanto remova o Sparkle da lista de dependências de pacote do target MAS e da fase de build do Frameworks.

③ Limpar o arquivo temporário Info.plist

A Duplicação cria um arquivo temporário Info.plist como FocusTimer copy-Info.plist. Como vamos criar um Info.plist dedicado para MAS separadamente na Parte 2, exclua esses arquivos temporários — tanto a referência do projeto quanto o arquivo real.

④ Atualizar as configurações de build do target MAS

Você precisa alinhar as configurações de build como o identificador de bundle, o caminho do Info.plist e o caminho dos entitlements para o MAS. Faremos isso tudo de uma vez na Parte 2, depois de criar os arquivos de configuração dedicados.

Os itens ① e ② podem ser tratados principalmente nas telas de associação de target e dependências de pacote na interface do Xcode, mas se as referências duplicadas deixadas pelo processo de duplicação não forem limpas perfeitamente, às vezes pode ser necessário olhar diretamente para o arquivo do projeto (.pbxproj). Uma vez que você achar que a limpeza está feita, a maneira mais confiável de verificar é compilar os dois targets separadamente e verificar se o import Sparkle quebra no build do target MAS (a Parte 2 cobre essa ramificação de código).

Resumo da Parte 1

Se você acompanhou até aqui, agora tem:

  • ✅ Um Bundle ID exclusivo para MAS (com.example.FocusTimer.mas) registrado no Apple Developer Portal
  • ✅ Um target de build FocusTimer MAS criado por duplicação
  • ✅ Entendimento do trabalho de limpeza que a duplicação deixa para trás (associação de código-fonte, dependência do Sparkle, arquivos temporários)

O “contêiner” — o target — foi criado. Mas esse target ainda é essencialmente apenas uma cópia do FocusTimer; ele ainda não se tornou verdadeiramente “específico para MAS”. Um build MAS precisa usar entitlements diferentes e um Info.plist diferente do build de distribuição direta, e o código também precisa ser ramificado para não quebrar quando o Sparkle estiver ausente.

Na próxima parte, cobriremos os arquivos de configuração e a ramificação de código que separam os dois canais.