Pour prendre en charge les anciennes versions de macOS, compilez avec un SDK qui connaît les nouvelles API, définissez une cible de déploiement minimale prise en charge et protégez les nouveaux appels avec des vérifications de disponibilité et des solutions de repli fonctionnelles. Ce guide permet à une petite fenêtre native de notes de rester utilisable sous macOS 12 tout en adoptant une vue de macOS 14.

Quels paramètres de compatibilité doivent concorder ?

Pour un développeur d’applications Mac, l’objectif concret est de produire une application qui se lance sur la version la plus ancienne prise en charge et utilise les nouvelles fonctionnalités lorsqu’elles sont disponibles. Commencez par distinguer trois éléments : le compilateur comprend le langage source, le SDK fournit les déclarations d’API et la cible de déploiement définit l’environnement d’exécution le plus ancien visé. La référence des paramètres de compilation d’Apple définit SDKROOT comme le SDK dont les en-têtes et bibliothèques participent à la compilation. Référence des paramètres de compilation

Un SDK récent et une cible de déploiement plus ancienne peuvent fonctionner ensemble. Clang documente explicitement la compilation avec le SDK le plus récent tout en sélectionnant une version minimale antérieure de macOS avec -mmacos-version-min=. Cette combinaison exige tout de même des vérifications de disponibilité pour les API introduites après la version minimale. Extensions du langage Clang

Choisissez une version minimale réellement prise en charge par la version de Xcode que vous avez sélectionnée. Le tableau de compatibilité d’Apple distingue la version de macOS requise pour exécuter Xcode des cibles de déploiement qu’il peut compiler. Si la version minimale souhaitée sort de la plage prise en charge par cette chaîne d’outils, choisissez une chaîne d’outils appropriée et prise en charge, ou révisez la version minimale de l’application avant d’implémenter des solutions de repli. SDK et configuration système requise

Pour cet exemple fictif, le contrat est macOS 12 ou ultérieur, un SDK contenant les API de macOS 14 et une fenêtre de notes avec deux états : une collection vide et une note d’exemple. La présentation plus récente de l’état vide est facultative ; l’affichage de la note reste disponible sur la version de base.

Définir la version minimale dans les deux configurations de compilation

Dans les Build Settings de la cible de l’application, définissez MACOSX_DEPLOYMENT_TARGET sur 12.0 pour Debug et Release. Si vous gérez les paramètres dans des fichiers de configuration, voici un fragment de configuration complet, et non un programme :

MACOSX_DEPLOYMENT_TARGET = 12.0

Attribuez le fichier de configuration aux configurations voulues dans l’onglet Info du projet. Vérifiez ensuite la valeur finale de la cible : Apple indique que les paramètres de la cible peuvent remplacer les valeurs fournies par un fichier de configuration attribué. Ajouter simplement le fichier au projet ne suffit pas à rendre ses paramètres effectifs. Ajouter un fichier de configuration de compilation à votre projet

Comment ajouter une API récente sans relever la version minimale de l’application ?

La vérification Swift if #available contrôle la disponibilité à l’exécution et indique au compilateur quelles API peuvent être utilisées dans cette branche. Conservez la solution de repli dans la même décision afin que l’ancien système dispose d’une implémentation complète. Le * obligatoire couvre les plateformes qui ne sont pas nommées explicitement ; il ne rend pas l’API macOS nommée disponible sur les versions antérieures. Flux de contrôle — Vérifier la disponibilité des API

La ContentUnavailableView d’Apple est disponible à partir de macOS 14 et prend en charge les présentations de contenu vide. Ici, une petite VStack fournit la présentation correspondante sur macOS 12 et 13. Les deux branches indiquent la même prochaine action. ContentUnavailableView

Application SwiftUI fictive complète

Créez un projet d’application macOS nommé CompatibilityNotes, choisissez Swift et SwiftUI, puis définissez sa cible de déploiement sur 12.0. Utilisez un SDK contenant les API de macOS 14. Remplacez les déclarations générées de l’application et de la vue de contenu par le fichier unique suivant, en veillant à ce que la cible ne contienne qu’une seule déclaration @main. Aucun asset, package ni fichier d’entrée n’est nécessaire.

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.")
        }
    }
}

Le comportement attendu est simple : macOS 12 et 13 affichent le texte de repli ; macOS 14 et les versions ultérieures affichent ContentUnavailableView. Sélectionner Add sample note affiche la note fictive. Sélectionner Reset sample rétablit l’état vide. Cette démonstration conserve ses données en mémoire et n’enregistre pas les notes entre les lancements.

L’annotation @available limite l’utilisation de l’assistant à macOS 14 et aux versions ultérieures. La branche #available de l’appelant satisfait cette restriction. Une annotation décrit la disponibilité d’une déclaration ; elle ne fournit pas d’implémentation pour les anciennes versions. Attributs — available

Cet exemple a été examiné à l’aide de la documentation récupérée, mais il n’a pas été compilé ni exécuté : l’environnement d’exécution disponible était Linux et ne disposait ni de Swift ni de Clang. Des vérifications de compilation et d’exécution restent nécessaires sur macOS.

À quoi servent les vérifications de disponibilité lors de l’édition de liens ?

Une branche d’exécution n’est utile que si l’exécutable peut être chargé. Clang indique que les déclarations annotées comme disponibles et introduites après la cible de déploiement peuvent normalement être liées faiblement. Une référence faible permet que la déclaration soit absente à l’exécution ; l’application doit tout de même éviter de l’utiliser lorsqu’elle manque. La cible de déploiement et les déclarations visibles lors de la compilation comptent donc autant que la branche. Attributs dans Clang — disponibilité

Pour cet exemple, liez SwiftUI normalement : ce framework est déjà présent sur la version de base macOS 12. La limite de compatibilité entoure la nouvelle vue. Si un framework entièrement nouveau est absent de votre version de base, évaluez la dépendance au framework elle-même ; protéger un appel ne règle pas toutes les exigences de chargement. Le guide d’Apple sur l’édition de liens faibles explique la différence entre autoriser l’absence d’un symbole et utiliser effectivement ce symbole. Frameworks et édition de liens faibles

Utilisez la syntaxe de disponibilité correspondant au langage source :

Langage source Condition de disponibilité à l’exécution
Swift if #available(macOS 14.0, *)
Objective-C ou Objective-C++ if (@available(macOS 14.0, *))
C ou C++ avec la prise en charge des plateformes Apple par Clang if (__builtin_available(macOS 14.0, *))

Clang documente la syntaxe Objective-C et le builtin C/C++, ainsi que -Wunguarded-availability. Activez les diagnostics de disponibilité pour le code de la famille C et examinez les avertissements avant de considérer qu’une cible de déploiement plus ancienne est utilisable. Extensions du langage Clang

Que faire si le SDK de compilation ne contient pas l’API ?

Une vérification à l’exécution ne peut pas fournir une déclaration absente du SDK. Si le compilateur ne reconnaît pas ContentUnavailableView, compilez avec un SDK qui la contient. Si le projet doit aussi être compilé avec d’anciens SDK, maintenez une organisation conditionnelle délibérée du code source qui exclut les références non prises en charge de ces compilations.

En Swift, #if compiler(...) vérifie la version du compilateur, tandis que #if canImport(...) vérifie si un module peut être importé. Aucune de ces conditions n’établit qu’un membre particulier d’un module existant est disponible sur le Mac à l’exécution. Utilisez ces conditions de compilation aux fins documentées, puis les vérifications de disponibilité à l’exécution lorsque nécessaire. Instructions — bloc de compilation conditionnelle

Quand la stabilité de l’ABI Swift et les limites des bibliothèques C++ changent-elles la réponse ?

La stabilité de l’ABI Swift ne rend pas disponibles sur les anciens systèmes toutes les fonctionnalités récentes de la bibliothèque standard ou de l’environnement d’exécution. Les explications de Swift distinguent le mode du langage de la prise en charge à l’exécution : le changement de mode du langage est un choix effectué à la compilation, tandis que les fonctionnalités qui nécessitent de nouvelles fonctions de l’environnement d’exécution peuvent avoir des exigences liées au système d’exploitation. Respectez la disponibilité de la fonctionnalité que vous adoptez. Évolution de Swift sur les plateformes Apple après la stabilité de l’ABI

Le C++ distingue lui aussi la prise en charge par le compilateur de celle par la bibliothèque. Apple fournit Apple Clang par l’intermédiaire de Xcode, et l’environnement d’exécution libc++ par l’intermédiaire de ses SDK et systèmes d’exploitation. Son tableau des fonctionnalités indique des exigences qui peuvent dépendre de la bibliothèque partagée fournie par le système d’exploitation. Choisir -std=c++17 ou -std=c++20 n’installe pas cette fonctionnalité d’exécution sur le Mac de l’utilisateur. Prise en charge du langage C++

La conversion numérique en est un exemple utile : Apple indique macOS 10.15 comme version minimale pour std::to_chars et std::from_chars avec des types entiers, mais macOS 13.3 pour leurs surcharges en virgule flottante. Une application macOS 12 a donc besoin d’une implémentation de conversion compatible si elle doit sinon utiliser ces surcharges en virgule flottante. Vérifiez la surcharge exacte, et pas seulement l’en-tête ou la version de la norme C++. Prise en charge du langage C++

Ne partez pas du principe qu’encadrer chaque appel de bibliothèque C++ par __builtin_available suffit. Les attributs de disponibilité de Clang comprennent une forme strict qui rejette l’utilisation lorsque la cible est une plateforme antérieure, et libc++ utilise des annotations de disponibilité pour signaler à la compilation l’absence de prise en charge à l’exécution. Si les en-têtes sélectionnés rejettent l’opération, utilisez une implémentation compatible avec la version de base ou relevez la version minimale. Attributs dans Clang — disponibilité, Documentation des fournisseurs — annotations de disponibilité

Préservez la configuration ABI de la bibliothèque d’une dépendance à l’autre. LLVM documente des garanties ABI précises ainsi que leurs limites, notamment le fait que le code compilé avec des en-têtes libc++ récents peut ne pas fonctionner avec une ancienne bibliothèque. Ses options de configuration qui modifient l’ABI sont des paramètres réservés aux fournisseurs. Les modifier dans le code de l’application ne remplace pas un environnement d’exécution compatible. Garanties ABI de libc++

Comment diagnostiquer et vérifier la compilation finale ?

Appuyez-vous sur l’étape où survient l’échec pour choisir la prochaine piste à examiner. Cet outil d’aide à la décision suit les limites du compilateur, de la disponibilité et de l’exécution décrites ci-dessus ; il s’agit d’une séquence de diagnostic, et non de l’affirmation que l’exemple a réussi les tests.

Symptôme Premier élément à examiner Prochaine étape pratique
Le nom de la nouvelle API est inconnu Déclarations du SDK Sélectionnez un SDK qui contient l’API.
L’API est plus récente que la cible de déploiement Utilisation non protégée Ajoutez une limite de disponibilité prise en charge et une solution de repli.
L’opération de bibliothèque C++ est toujours rejetée Surcharge exacte et exigences de la bibliothèque Utilisez une implémentation compatible ou révisez la version minimale.
L’application échoue avant l’affichage de sa fenêtre Exigences de chargement et dépendances Examinez la bibliothèque ou le symbole manquant signalé.
Seule la nouvelle fonctionnalité échoue Travail effectué hors de la branche protégée Examinez la création, l’initialisation et les appels aux assistants.
La compilation fonctionne sur une seule famille de processeurs Architectures de l’exécutable et des dépendances Compilez et testez les tranches d’architecture requises.

La prise en charge des architectures est une exigence supplémentaire. Le guide d’Apple sur les binaires universels traite de arm64 et de x86_64, notamment des bibliothèques et frameworks compilés. Vérifiez les dépendances ainsi que l’exécutable de l’application ; une compilation réussie pour votre Mac de développement n’établit la compatibilité que de cette compilation. Créer un binaire macOS universel

Pour cet exemple, compilez la configuration de distribution et testez l’application obtenue sous macOS 12, macOS 13 et macOS 14 ou ultérieur, sur du matériel pris en charge. Sur chaque système, lancez l’application avec la collection vide, ajoutez la note d’exemple et réinitialisez-la. Les anciennes versions doivent conserver toutes les interactions grâce à la solution de repli. Testez chaque architecture que vous comptez distribuer.

À mesure que l’application évolue, gardez explicite la limite de prise en charge : une cible de déploiement prise en charge, des déclarations issues d’un SDK approprié, des nouvelles fonctionnalités protégées et une implémentation utile pour la version de base. Chaque nouvelle API ou dépendance doit respecter ce même contrat avant d’être intégrée à la compilation.

Sources