Para oferecer suporte a versões mais antigas do macOS, compile com um SDK que conheça as APIs mais recentes, defina um destino mínimo de implantação compatível e proteja as chamadas mais recentes com verificações de disponibilidade e alternativas funcionais. Este passo a passo mantém uma pequena janela nativa de notas utilizável no macOS 12 enquanto adota uma visualização do macOS 14.

Quais configurações de compatibilidade precisam estar alinhadas?

Para quem desenvolve apps para Mac, a tarefa concreta é produzir um único app que seja iniciado na versão mais antiga compatível e use os recursos mais recentes quando disponíveis. Comece separando três elementos: o compilador entende a linguagem do código-fonte, o SDK fornece as declarações de API e o destino de implantação identifica o runtime mais antigo pretendido. A referência de configurações de build da Apple define SDKROOT como o SDK cujos cabeçalhos e bibliotecas participam do build. Referência de configurações de build

Um SDK mais recente pode funcionar com um destino de implantação mais antigo. O Clang documenta explicitamente a compilação com o SDK mais recente e a seleção de um mínimo anterior do macOS por meio de -mmacos-version-min=. Essa combinação ainda exige verificações de disponibilidade para APIs introduzidas depois da versão mínima. Extensões da linguagem Clang

Escolha uma versão mínima compatível com a versão do Xcode selecionada. A tabela de compatibilidade da Apple distingue a versão do macOS necessária para executar o Xcode dos destinos de implantação para os quais ele pode compilar. Se a versão mínima desejada estiver fora do intervalo compatível com essa cadeia de ferramentas, escolha uma cadeia de ferramentas compatível ou revise o mínimo do app antes de implementar alternativas. SDKs e requisitos do sistema

Neste exemplo sintético, o requisito é macOS 12 ou posterior, um SDK que contenha APIs do macOS 14 e uma janela de notas com dois estados: uma coleção vazia e uma nota de exemplo. A apresentação mais recente para o estado vazio é opcional; a exibição da nota continua disponível na versão mínima compatível.

Defina o mínimo nas duas configurações de build

Em Build Settings do destino do app, defina MACOSX_DEPLOYMENT_TARGET como 12.0 para Debug e Release. Se você gerencia as configurações em arquivos de configuração, o trecho a seguir é uma configuração completa, não um programa:

MACOSX_DEPLOYMENT_TARGET = 12.0

Atribua o arquivo de configuração às configurações pretendidas na aba Info do projeto. Depois, confira o valor final do destino: a Apple documenta que as configurações do destino podem substituir os valores fornecidos por um arquivo de configuração atribuído. Apenas adicionar o arquivo ao projeto não faz com que suas configurações tenham efeito. Como adicionar um arquivo de configuração de build ao projeto

Como adicionar uma API mais recente sem elevar o mínimo do app?

A verificação if #available do Swift verifica a disponibilidade em runtime e informa ao compilador quais APIs podem ser usadas dentro desse bloco. Mantenha a alternativa na mesma decisão para que o sistema mais antigo tenha uma implementação completa. O * obrigatório abrange plataformas que não foram nomeadas explicitamente; ele não torna a API indicada do macOS disponível em versões anteriores. Fluxo de controle — Verificação de disponibilidade de API

O ContentUnavailableView da Apple está disponível a partir do macOS 14 e oferece suporte a apresentações de conteúdo vazio. Aqui, um pequeno VStack fornece a apresentação correspondente no macOS 12 e 13. Ambos os blocos indicam a mesma próxima ação. ContentUnavailableView

App SwiftUI sintético completo

Crie um projeto de app para macOS chamado CompatibilityNotes, escolha Swift e SwiftUI e defina o destino de implantação como 12.0. Use um SDK que contenha APIs do macOS 14. Substitua as declarações geradas do app e da visualização de conteúdo pelo arquivo único a seguir, garantindo que o destino contenha apenas uma declaração @main. Não são necessários recursos, pacotes nem arquivos de entrada.

import SwiftUI

@main
struct CompatibilityNotesApp: App {
    var body: some Scene {
        WindowGroup {
            NotesView()
        }
    }
}

struct NotesView: View {
    @State private var notes: [String] = []

    var body: some View {
        VStack(spacing: 16) {
            if notes.isEmpty {
                emptyState
            } else {
                List(notes, id: \.self) { note in
                    Text(note)
                }
            }

            Button(notes.isEmpty ? "Add sample note" : "Reset sample") {
                if notes.isEmpty {
                    notes = ["Sample note: arrange the blue shapes."]
                } else {
                    notes = []
                }
            }
        }
        .padding()
        .frame(minWidth: 360, minHeight: 240)
    }

    @ViewBuilder
    private var emptyState: some View {
        if #available(macOS 14.0, *) {
            ModernEmptyState()
        } else {
            VStack(spacing: 8) {
                Text("No notes")
                    .font(.headline)
                Text("Add a sample note to begin.")
            }
        }
    }
}

@available(macOS 14.0, *)
private struct ModernEmptyState: View {
    var body: some View {
        ContentUnavailableView {
            Label("No notes", systemImage: "note.text")
        } description: {
            Text("Add a sample note to begin.")
        }
    }
}

O comportamento esperado é simples: no macOS 12 e 13, o texto da alternativa é exibido; no macOS 14 e posteriores, é exibido ContentUnavailableView. Ao selecionar Add sample note, a nota sintética é exibida. Ao selecionar Reset sample, o estado vazio é restaurado. Esta demonstração mantém os dados na memória e não salva as notas entre inicializações.

A anotação @available restringe o uso do auxiliar ao macOS 14 e posteriores. O bloco #available do chamador atende a essa restrição. Uma anotação descreve a disponibilidade de uma declaração; ela não fornece uma implementação para sistemas mais antigos. Atributos — available

Este exemplo foi analisado com base na documentação consultada, mas não foi compilado nem executado: o ambiente de execução disponível era Linux e não tinha Swift nem Clang instalados. Ainda é necessário fazer verificações de build e runtime no macOS.

O que as verificações de disponibilidade resolvem no momento da vinculação?

Uma ramificação em runtime só é útil se o executável puder ser carregado. O Clang documenta que declarações anotadas com disponibilidade, introduzidas depois do destino de implantação, normalmente podem ser vinculadas de forma fraca. Uma referência fraca permite que a declaração não esteja presente em runtime; ainda assim, o app precisa evitar usá-la quando estiver ausente. Portanto, o destino de implantação e as declarações visíveis durante a compilação também importam, além do bloco condicional. Atributos no Clang — disponibilidade

No exemplo, mantenha o SwiftUI vinculado normalmente: a estrutura já está presente na versão mínima do macOS 12. O limite de compatibilidade envolve a visualização mais recente. Se uma estrutura totalmente nova não estiver presente na versão mínima, avalie a própria dependência dessa estrutura; proteger uma chamada não resolve todos os requisitos de carregamento. O guia de vinculação fraca da Apple explica a diferença entre permitir que um símbolo esteja ausente e realmente usar esse símbolo. Estruturas e vinculação fraca

Use a sintaxe de disponibilidade correspondente à linguagem do código-fonte:

Linguagem do código-fonte Condição de disponibilidade em runtime
Swift if #available(macOS 14.0, *)
Objective-C ou Objective-C++ if (@available(macOS 14.0, *))
C ou C++ com suporte do Clang para plataformas da Apple if (__builtin_available(macOS 14.0, *))

O Clang documenta a sintaxe de Objective-C e o recurso interno de C/C++, além de -Wunguarded-availability. Ative os diagnósticos de disponibilidade para código da família C e investigue os avisos antes de considerar utilizável um destino de implantação mais antigo. Extensões da linguagem Clang

E se o SDK usado no build não contiver a API?

Uma verificação em runtime não pode fornecer uma declaração ausente do SDK. Se o compilador não conseguir resolver ContentUnavailableView, compile com um SDK que a contenha. Se o projeto também precisar compilar com SDKs mais antigos, mantenha uma organização condicional do código-fonte que exclua deliberadamente as referências incompatíveis desses builds.

#if compiler(...) do Swift verifica a versão do compilador, enquanto #if canImport(...) verifica se um módulo pode ser importado. Nenhuma das duas condições determina se um membro específico de um módulo existente está disponível no Mac em execução. Use essas condições de compilação para os fins documentados e, quando necessário, use verificações de disponibilidade em runtime. Instruções — Bloco de compilação condicional

Em que situações os limites da ABI do Swift e da biblioteca C++ mudam a resposta?

A estabilidade da ABI do Swift não torna todos os recursos mais recentes da biblioteca padrão ou do runtime disponíveis em sistemas antigos. A explicação do Swift distingue o modo da linguagem do suporte do runtime: mudar o modo da linguagem é uma escolha de compilação, enquanto recursos que precisam de novas funcionalidades de runtime podem ter requisitos de sistema operacional. Siga os requisitos de disponibilidade do recurso específico que você adotar. Evolução do Swift nas plataformas da Apple após a estabilidade da ABI

O C++ apresenta uma separação semelhante entre o suporte do compilador e o da biblioteca. A Apple fornece o Apple Clang por meio do Xcode e o runtime libc++ por meio dos SDKs e sistemas operacionais. A tabela de recursos identifica requisitos que podem depender da biblioteca compartilhada fornecida pelo sistema operacional. Selecionar -std=c++17 ou -std=c++20 não instala essa funcionalidade de runtime no Mac do usuário. Suporte à linguagem C++

Um exemplo útil é a conversão numérica: a Apple indica o macOS 10.15 como versão mínima para std::to_chars e std::from_chars com tipos inteiros, mas exige o macOS 13.3 para as sobrecargas de ponto flutuante. Portanto, um app para macOS 12 precisa de uma implementação de conversão compatível se, de outro modo, exigir essas sobrecargas de ponto flutuante. Confira a sobrecarga exata, não apenas o cabeçalho ou a versão do padrão C++. Suporte à linguagem C++

Não presuma que envolver todas as chamadas de biblioteca C++ em __builtin_available seja suficiente. Os atributos de disponibilidade do Clang incluem uma forma strict que rejeita o uso ao compilar para uma plataforma mais antiga, e o libc++ usa marcações de disponibilidade para diagnosticar durante a compilação a ausência de suporte no runtime. Quando os cabeçalhos selecionados rejeitarem a operação, use uma implementação compatível com a versão mínima ou eleve o mínimo. Atributos no Clang — disponibilidade, Documentação do fornecedor — Marcações de disponibilidade

Preserve a configuração da ABI da biblioteca entre dependências. O LLVM documenta garantias específicas da ABI e seus limites, inclusive que o código compilado com cabeçalhos mais recentes do libc++ pode não funcionar com uma biblioteca mais antiga. Os sinalizadores de configuração que alteram a ABI são controles do fornecedor. Alterá-los no código do app não substitui um runtime compatível. Garantias de ABI do libc++

Como diagnosticar e verificar o build concluído?

Use a etapa em que ocorreu a falha para escolher o que investigar em seguida. Este guia de decisão acompanha os limites do compilador, da disponibilidade e do runtime apresentados acima; é uma sequência de diagnóstico, não uma afirmação de que o exemplo passou nos testes.

Sintoma Investigue primeiro Próxima etapa prática
Nome da API mais recente desconhecido Declarações do SDK Selecione um SDK que contenha a API.
API mais recente que o destino de implantação Uso desprotegido Adicione um limite de disponibilidade compatível e uma alternativa.
Operação da biblioteca C++ continua sendo rejeitada Requisitos da sobrecarga e da biblioteca específica Use uma implementação compatível ou revise a versão mínima.
O app falha antes de a janela aparecer Requisitos de carregamento e dependências Examine a biblioteca ou o símbolo ausente informado.
Apenas o recurso mais recente falha Trabalho realizado fora do bloco protegido Inspecione as chamadas de criação, inicialização e auxiliares.
O build funciona apenas em uma família de processadores Arquiteturas do executável e das dependências Compile e teste as fatias de arquitetura necessárias.

O suporte à arquitetura é um requisito adicional. As orientações da Apple sobre binários universais abrangem arm64 e x86_64, incluindo bibliotecas e estruturas compiladas. Verifique as dependências e o executável do app; um build bem-sucedido no Mac de desenvolvimento só comprova a compatibilidade daquele build. Como criar um binário universal para macOS

Para este exemplo, compile a configuração de lançamento e teste o app resultante no macOS 12, macOS 13 e macOS 14 ou posterior, em hardware compatível. Em cada sistema, inicie o app com a coleção vazia, adicione a nota de exemplo e redefina-a. As versões mais antigas devem manter a interação completa por meio da alternativa. Teste cada arquitetura que pretende distribuir.

Mantenha explícito o limite de compatibilidade à medida que o app cresce: um destino de implantação compatível, declarações de um SDK apropriado, funcionalidades mais recentes protegidas e uma implementação útil para a versão mínima. Cada API ou dependência adicional deve atender ao mesmo contrato antes de fazer parte do build.

Fontes