← Todos os Posts

macOS 27 nega acesso a containers de outro time sem perguntar

No macOS, containerURL(forSecurityApplicationGroupIdentifier:) devolve uma URL aparentemente válida para um app group sobre o qual você não tem entitlement algum. A Apple documenta isso sem rodeios: no iOS o método retorna nil para um identificador inválido, mas no macOS “uma URL no formato esperado é sempre retornada, mesmo que o app group seja inválido.”4 No macOS 27, esse comportamento colide com uma restrição nova.

O macOS 27 parou de perguntar ao usuário antes de negar acesso a containers de outro time. Antes, ler arquivos no container de dados ou no container de app group de outro time de desenvolvimento gerava um prompt de autorização. Agora isso falha por padrão, e só dá para reverter se o usuário achar a entrada em Privacidade e Segurança.1

Esses dois fatos se combinam numa falha que não emite sinal algum no ponto em que acontece. Nenhuma caixa de diálogo aparece. O método devolve uma URL em vez de nil. O caminho parece exatamente o que você esperava. A negação só aparece depois, na operação de arquivo, onde parece um arquivo que não existe.

Resumo

O macOS 27 remove o prompt de autorização do usuário para acessar containers de dados e containers de app group de outros times, nega esse acesso por padrão e move o controle do usuário para as configurações de Privacidade e Segurança.1 A mudança está catalogada sob System Integrity Protection como um recurso novo, não como correção de bug. A própria orientação da Apple sobre containers de app group ainda descreve o comportamento de prompt que o macOS 27 eliminou. Como a API do macOS devolve uma URL bem formada mesmo para grupos que você não consegue acessar, a negação aparece na hora da leitura, e não na chamada da API. O acesso dentro do mesmo time não muda: a fronteira é o Team ID.

O que mudou

As notas de versão do macOS 27 trazem uma frase sob System Integrity Protection:1

“Acessar arquivos em containers de dados e containers de app group de outros times de desenvolvimento não solicita mais autorização ao usuário; tais acessos são negados por padrão e podem ser gerenciados pelo usuário nas configurações de Privacidade e Segurança.”

Radar 161835690. Duas orações, duas mudanças distintas.

A primeira remove um prompt. A segunda estabelece a negação como padrão. A cobertura dessa mudança costuma abrir pela negação, que é a metade menos interessante. Padrões mais restritivos são rotina. Um prompt que desaparece muda o formato da falha que seus usuários vivenciam, e o formato do relatório de bug que chega até você.

Antes, o usuário via um diálogo e tomava uma decisão. Se recusasse, seu app sabia de algo que um humano tinha escolhido. Agora ninguém é consultado. O acesso simplesmente não acontece, e o único caminho para habilitá-lo passa por um painel de configurações que o usuário não tem motivo nenhum para visitar, a menos que alguém mande.

O comportamento anterior que a documentação da Apple ainda descreve

A restrição se assenta sobre uma proteção que chegou duas versões antes. O guia da Apple sobre containers de app group afirma:2

“No macOS 15 e posterior, os containers de app group oferecem [System Integrity Protection] para os arquivos locais do seu app mesmo que ele não tenha a capacidade de isolamento de apps. Esses containers de app group limitam o acesso de apps que não fazem parte do app group. Apps fora do app group que tentam acessar locais dentro de um app group ou de um container de dados resultam em um prompt ao usuário solicitando sua autorização.”

Essa página descreve o prompt no presente. Até o momento em que este texto foi escrito, a Apple não a atualizou para o macOS 27.

A consequência prática: quem esbarra nisso, procura na documentação e encontra a página oficial vai ler que um diálogo de autorização vai aparecer. Não vai. A pessoa então vai concluir, com razão, que o prompt está quebrado ou que os entitlements estão mal configurados, e vai gastar tempo no lugar errado.

Vale conferir você mesmo o estado atual dessa página em vez de confiar na data deste post. Documentação acaba se atualizando.

Por que a falha só aparece depois

O comportamento da API é a parte que transforma uma mudança de política em problema de depuração.

A referência da Apple para containerURL(forSecurityApplicationGroupIdentifier:) é explícita sobre a diferença entre plataformas:4

“Uma URL indicando a localização do diretório compartilhado do grupo no sistema de arquivos. No iOS, o valor é nil quando o identificador do grupo é inválido. No macOS, uma URL no formato esperado é sempre retornada, mesmo que o app group seja inválido, então certifique-se de testar se você consegue acessar o diretório subjacente antes de tentar usá-lo.”

A seção de discussão repete o aviso: chamar o método com um identificador de grupo para o qual você não tem entitlement ainda devolve uma URL no formato esperado, mas o diretório não existe e um app em ambiente isolado não consegue criá-lo.4

Ou seja, o padrão defensivo mais comum não serve para nada aqui:

// This guard passes on macOS regardless of entitlement.
guard let url = FileManager.default.containerURL(
    forSecurityApplicationGroupIdentifier: "ABCDE12345.com.example.shared"
) else {
    return  // never taken on macOS
}

A URL é bem formada. Ela aponta para ~/Library/Group Containers/<team>.<group>, exatamente onde esse container ficaria. Tudo parece dar certo até uma operação de arquivo rodar em cima dela.

A solução é tentar a leitura e tratar a falha, em vez de perguntar se ela daria certo:

let fm = FileManager.default
guard let url = fm.containerURL(
    forSecurityApplicationGroupIdentifier: groupID
) else { return }  // macOS never takes this branch

do {
    _ = try fm.contentsOfDirectory(atPath: url.path)
    // Reached the container.
} catch {
    // On macOS 27 a cross-team denial lands here.
    // No prompt fired. The user was never asked.
    presentContainerUnavailable(error)
}

Resista à tentação de recorrer a isReadableFile(atPath:) como verificação prévia. A Apple desaconselha essa classe inteira de teste:3

“Tentar condicionar o comportamento ao estado atual do sistema de arquivos ou de um arquivo específico nele não é recomendado. Fazer isso pode causar comportamento estranho ou condições de corrida. É muito melhor tentar uma operação (como carregar um arquivo ou criar um diretório), verificar se houve erros e tratá-los com elegância do que tentar descobrir de antemão se a operação vai dar certo.”

Há um segundo motivo específico dessa mudança. A mesma página observa que isReadableFile(atPath:) “usa o ID de usuário real e o ID de grupo” para decidir se algo é legível.3 Isso é avaliação de permissão POSIX. Negar um container de outro time é uma decisão de política numa camada acima do POSIX, então uma checagem de bits de permissão não necessariamente reflete essa negação. Uma verificação prévia que devolve true antes de uma leitura que falha assim mesmo é pior do que verificação nenhuma, porque afasta ainda mais a surpresa da causa.

Quem leu canOpenURL está obsoleto no iOS 27 vai reconhecer o formato. A Apple segue removendo a capacidade de perguntar antes e deixando a capacidade de tentar. Tentar e tratar está virando a resposta geral, não uma solução alternativa para uma API isolada.

Repare na assimetria entre as plataformas. O iOS devolve nil e falha honestamente no local da chamada. O macOS devolve uma URL e adia a falha. A mudança do macOS 27 remove o prompt, que era o único sinal restante justamente na plataforma que já era a mais silenciosa das duas.

Mais uma armadilha: a orientação da Apple manda sempre usar a URL que esse método devolve em vez de montar ~/Library/Group Containers/... na mão, porque o local pode mudar em versões futuras.4 Quem fixou esse caminho no código não tem chamada de método nenhuma para verificar, nem lugar onde colocar o teste de leitura.

Provavelmente não depende do SDK

Uma pergunta natural é se ficar num SDK mais antigo adia a mudança. As notas de versão não dizem.

O que elas mostram é um padrão. As notas do macOS 27 usam linguagem explícita de condicionamento ao SDK em 11 entradas distintas: “Em apps compilados com o SDK do macOS 27.0”, “Em apps compilados com os SDKs 27.0”, “Quando seu projeto tem um deployment target mínimo inferior a 27.0.”1 A Apple qualifica essas mudanças quando a qualificação se aplica.

A entrada sobre containers não traz qualificador algum. Na leitura razoável de que essa ausência é deliberada, a restrição é uma política em nível de sistema operacional que se aplica a todo binário rodando no macOS 27, independentemente de qual SDK o compilou.

Trate isso como uma inferência forte, não como fato declarado, porque a Apple não disse isso diretamente. A consequência operacional é a mesma de qualquer jeito: não conte com um SDK mais antigo como mitigação. Conte com o acesso falhando.

Quem isso realmente atinge

O acesso dentro do mesmo time continua intocado. As regras da Apple tornam a fronteira do Team ID algo estrutural, não acidental: “Times de desenvolvimento diferentes não podem usar o mesmo app group”, enquanto um único time pode compartilhar um grupo entre seus próprios apps e processos de apoio.2

Os apps que quebram são os que atravessam essa linha:

Ferramentas de migração e importação. Qualquer coisa que leia o container de um concorrente ou de um predecessor para importar dados do usuário. É o caso mais claro, e já estava atrás de um prompt que os usuários às vezes aprovavam.

Utilitários de backup e sincronização. Ferramentas que enumeram containers de apps para fazer backup agora pulam silenciosamente tudo que estiver fora do próprio time.

Apps companheiros que mudaram de dono. O caso mais traiçoeiro, porque nada no código muda. Dois apps são publicados sob um único Team ID e compartilham um grupo sem problema algum. Uma aquisição, uma divisão de time ou uma migração para outra conta de desenvolvedor coloca os dois sob Team IDs diferentes. O identificador do app group continua parecendo certo, a URL continua resolvendo, e os dados param de chegar.

Esse último caso merece atenção especial, porque o timing joga contra você. Mudanças de Team ID são conduzidas por quem cuida de assinatura e distribuição, e a dependência de container compartilhado costuma ser invisível desse ponto de vista. O código compila. Os dois apps são publicados. Nenhuma das suítes de teste detecta o problema, porque testes unitários não exercitam um container real de outro time e o CI roda os dois targets sob a identidade que a máquina de build tiver. A falha aparece em produção, nas máquinas dos usuários, na forma de dados que pararam de sincronizar entre dois apps que o usuário, com razão, considera o mesmo produto.

Se você está planejando uma migração de Team ID, a auditoria é mecânica: use grep para achar cada identificador de app group que seus targets declaram e, para cada um, liste quais bundles o leem. Todo identificador lido por bundles que vão parar sob Team IDs diferentes é uma quebra. É uma verificação de dez minutos antes da migração e um chamado de suporte depois dela.

A Apple lista o que pode participar de app groups: executáveis principais em estruturas de bundle, app extensions, App Clips e XPC Services.2 Cada um é um lugar onde isso pode aparecer.

Como diagnosticar

Vale rodar duas verificações antes de assumir que é bug no código.

Confirme que o sistema chegou a validar seus entitlements. A Apple documenta uma verificação em tempo de execução da flag de entitlements validados num processo em execução:2

sudo launchctl procinfo <pid>

Perfis de provisionamento criados antes de a autorização de app group existir podem não trazê-la, o que produz falhas de acesso idênticas à negação do macOS 27, mas com causa diferente. O Xcode atualiza os perfis quando “Automatically manage signing” está ligado e a build setting REGISTER_APP_GROUPS está como Yes.2

Saiba qual estilo de identificador você está usando. Grupos com o prefixo group. precisam estar incluídos no perfil de provisionamento do app. Grupos no formato <TeamID>.<group name> dispensam perfil, porque o sistema verifica o prefixo do identificador de time contra a identidade de assinatura, mas esse formato é exclusivo do macOS e não é suportado por Keychain Access Groups.2

Depois, confira Privacidade e Segurança, já que é ali que a nota de versão diz que o controle do usuário agora mora.1

Projetar para uma negação que você não pode solicitar

O prompt fazia trabalho de produto, não só de segurança. Ele avisava o usuário de que existia uma decisão a tomar, e avisava seu app quando essa decisão tinha sido tomada. Agora as duas tarefas sobram para você.

Sonde a fronteira, uma vez só. Tente uma leitura barata assim que resolver o container pela primeira vez, em vez de descobrir a negação no meio de um loop de sincronização. Uma falha lá no fundo de uma fila produz resultado parcial e um erro atribuído a qualquer arquivo que estivesse na vez. Uma tentativa no momento da resolução te dá um único ponto para ramificar e, ao contrário de uma verificação prévia de permissão, ela exercita o mesmo caminho que suas leituras reais vão percorrer.

Diga o que aconteceu, nos termos do usuário. “Não foi possível ler os dados” convida a um chamado de suporte sobre perda de dados. A mensagem correta nomeia a fronteira e o remédio: os dados pertencem a um app de outro desenvolvedor, o macOS bloqueia esse acesso por padrão, e a opção fica em Privacidade e Segurança. Ninguém consegue agir sobre uma falha que não consegue localizar.

Não tente de novo. Uma negação é um estado de política, não um erro transitório. Loops de backoff contra um container bloqueado gastam bateria e enchem os logs sem mudar nada. Falhe uma vez, exponha o estado e ofereça uma reverificação que o usuário possa disparar depois de mexer na configuração.

Reverifique na ativação, em vez de ficar consultando. Quando o usuário sai para mudar uma configuração e volta, esse é o momento de testar o acesso de novo. Consultar um caminho bloqueado num timer produz exatamente a mesma negação no intervalo que você escolher.

Pergunte se você precisa mesmo dessa leitura entre times. Essa é a incômoda, e muitas vezes a resposta certa. Uma ferramenta de migração que lê o container de um concorrente faz algo que a plataforma vem estreitando há três versões: submetido a isolamento no macOS 15, condicionado a prompt, e agora negado. Um caminho de exportação controlado pelo outro app, uma importação baseada em documentos via seletor de arquivos conduzido pelo usuário, ou um formato de intercâmbio documentado — todos sobrevivem a essa mudança. Leituras de container através da fronteira de Team ID estão numa trajetória, e essa trajetória aponta para um lado só.

O seletor de arquivos merece menção específica, porque é a saída que a plataforma pretende oferecer. Um usuário selecionando um arquivo concede acesso explicitamente pelo mecanismo de escopo de segurança, o que é um modelo de permissão mais sólido do que o prompt que acabou de sumir — e funciona hoje, sem mudança de configuração nenhuma.

Pontos principais

Para quem desenvolve apps macOS: - Nunca trate um retorno diferente de nil de containerURL(forSecurityApplicationGroupIdentifier:) como prova de acesso no macOS. Ele sempre devolve uma URL. Tente uma leitura e trate o erro. - Não use isReadableFile(atPath:) como verificação prévia. A Apple desaconselha prever resultados do sistema de arquivos, e o método avalia permissões POSIX, que uma negação na camada de política não precisa tocar. - Audite todo caminho de código que lê um container pertencente a outro Team ID. No macOS 27 ele falha sem perguntar a ninguém. - Se você fixou ~/Library/Group Containers/... no código, não tem chamada de método alguma para proteger. Migre para a API para ter onde colocar a verificação.

Para times que publicam ferramentas de migração ou backup: - Leituras entre times não estão mais a um prompt de distância. Projete considerando a negação como estado padrão e diga aos usuários onde fica a configuração. - A falha se apresenta como dado ausente, não como erro. Adicione mensagens explícitas, senão os usuários vão reportar isso como perda de dados.

Para quem vai mudar de Team ID: - Uma aquisição ou divisão de conta rompe silenciosamente o compartilhamento de app group entre apps que antes dividiam o mesmo prefixo de Team ID. Nada no código-fonte sinaliza isso.

Perguntas frequentes

Isso afeta apps que compartilham um container dentro de uma mesma conta de desenvolvedor?

Não. A fronteira é o Team ID. As regras da Apple já impedem que times diferentes usem o mesmo app group, e um único time pode compartilhar um grupo entre seus próprios apps, extensions, App Clips e XPC Services.2

Meus usuários vão ver um prompt que possam aprovar?

No macOS 27, não. A nota de versão afirma que esses acessos não solicitam mais autorização e são negados por padrão, com o gerenciamento transferido para as configurações de Privacidade e Segurança.1

Dá para escapar disso compilando com um SDK mais antigo?

Provavelmente não. As notas de versão usam linguagem explícita de condicionamento ao SDK em outras 11 mudanças e nenhuma para esta, o que sugere uma política em nível de sistema operacional aplicável a todos os binários.1 A Apple não disse isso abertamente, então trate como inferência forte e planeje que o acesso vai falhar.

Como distinguir isso de um problema de provisionamento?

Rode sudo launchctl procinfo <pid> e verifique se o sistema marcou a flag de entitlements validados no seu processo. Perfis de provisionamento mais antigos podem ser anteriores à autorização de app group e produzir uma falha parecida, mas com causa totalmente diferente.2

Por que a documentação ainda descreve um prompt?

O guia da Apple sobre containers de app group descreve o comportamento do macOS 15 e, até o momento em que este texto foi escrito, não tinha sido atualizado para a mudança do macOS 27.2 Verifique o estado atual dessa página em vez de confiar na data deste post.

Fontes


  1. Apple, “macOS 27 Golden Gate Beta 4 Release Notes.” System Integrity Protection, New Features: “Accessing files in other developer teams’ app data containers and app group containers no longer prompts the user for authorization; such accesses are denied by default and can be managed by the user in Privacy & Security settings.” Radar 161835690. Também é a fonte da linguagem de condicionamento ao SDK usada em outras 11 entradas e ausente nesta. O HTML é renderizado no lado do cliente; a cópia legível por máquina está em developer.apple.com/tutorials/data/documentation/macos-release-notes/macos-27-release-notes.json

  2. Apple, “Accessing app group containers in your existing macOS app.” Fonte do comportamento de prompt no macOS 15, da regra de que times de desenvolvimento diferentes não podem compartilhar um app group, da distinção entre os identificadores group. e <TeamID>.<group name>, da verificação de entitlements validados via sudo launchctl procinfo, da build setting REGISTER_APP_GROUPS e da lista de participantes elegíveis de containers. Consultado em 1º de agosto de 2026, ainda descrevendo o prompt. 

  3. Apple, “isReadableFile(atPath:).” Fonte da orientação contra prever o estado do sistema de arquivos: “It’s far better to attempt an operation (such as loading a file or creating a directory), check for errors, and handle those errors gracefully than it is to try to figure out ahead of time whether the operation will succeed.” Também é a fonte da observação de que o método “uses the real user ID and group ID, as opposed to the effective user and group IDs, to determine if the file is readable”, que é o motivo pelo qual uma verificação em nível POSIX não é um substituto confiável para uma negação na camada de política. 

  4. Apple, “containerURL(forSecurityApplicationGroupIdentifier:).” Valor de retorno: “In iOS, the value is nil when the group identifier is invalid. In macOS, a URL of the expected form is always returned, even if the app group is invalid, so be sure to test that you can access the underlying directory before attempting to use it.” Também é a fonte da orientação contra construir o caminho do container manualmente. 

Artigos relacionados

Menu Item Images Disappear in macOS 27 and iPadOS 27

macOS 27 and iPadOS 27 hide menu item images by default, and what disappears depends on the SDK you linked against. Thre…

13 min de leitura

O interpretador de fontes da Apple agora é Swift, e 13% mais rápido

A equipe de segurança da Apple reescreveu o interpretador de hinting TrueType de C para Swift com segurança de memória, …

9 min de leitura

The Mac App Store Won't Let Window Managers Exist. I Shipped One Anyway.

App Sandbox forbids the API every window tiler needs, and Apple said ship outside the store. 941 Tiles ships inside it: …

8 min de leitura