Avant App Intents, exposer des actions à Siri, c'était toute une affaire : il fallait un domaine SiriKit, un fichier .intentdefinition, un délégué dédié... Le résultat ? Du code verbeux, peu testable, et limité à une dizaine de domaines préétablis (paiement, réservation, messagerie, etc.). App Intents inverse complètement le modèle :
- Code Swift pur — fini les fichiers
.intentdefinition. Tout est défini dans des structures Swift.
- Domaines libres — vous décidez quelles actions exposer, sans schéma imposé d'en haut.
- Intégration native — un même
AppIntent alimente Siri, Spotlight, Raccourcis, le bouton Action, les widgets interactifs et Apple Intelligence. Un seul fichier, partout.
- Apple Intelligence — depuis iOS 18.4, et étendu massivement dans iOS 26, Apple Intelligence appelle directement vos
AppIntent à partir de requêtes en langage naturel.
- Visual Intelligence — sur iPhone 16 et ultérieur, vos intents peuvent même être déclenchés à partir d'objets reconnus par la caméra. Oui, c'est aussi cool que ça en a l'air.
En 2026, ne pas exposer d'App Intents, c'est rendre votre application invisible aux trois principaux points d'entrée du système : Siri, Spotlight et Apple Intelligence. Autant dire que c'est un luxe que peu d'apps peuvent se permettre.
Anatomie d'un AppIntent minimal
Un AppIntent, c'est tout simplement une structure Swift conforme au protocole AppIntent. Elle décrit ce que fait l'action (titre, description, paramètres) et comment elle l'exécute (méthode perform()). Voilà, c'est presque tout.
import AppIntents
struct AjouterTacheIntent: AppIntent {
static let title: LocalizedStringResource = "Ajouter une tâche"
static let description = IntentDescription(
"Crée une nouvelle tâche dans la liste sélectionnée."
)
@Parameter(title: "Titre de la tâche")
var titre: String
@Parameter(title: "Échéance", default: nil)
var echeance: Date?
func perform() async throws -> some IntentResult & ProvidesDialog {
let tache = try await TacheStore.shared.creer(
titre: titre,
echeance: echeance
)
return .result(
dialog: "J'ai ajouté « \(tache.titre) » à votre liste."
)
}
}
Trois éléments sont obligatoires :
title : le nom localisé affiché dans Raccourcis et Siri.
perform() : la logique métier, exécutée dans un contexte async throws.
- Au moins un type de retour conforme à
IntentResult.
Une fois le fichier compilé dans la cible principale de votre app, l'intent apparaît automatiquement dans Raccourcis. Aucun enregistrement manuel — rien à cocher dans le projet, aucun plist à modifier.
Les paramètres : le cœur de l'expérience
Les paramètres déterminent vraiment la richesse de l'intégration. Un paramètre bien typé permet à Siri de poser la bonne question, et à Apple Intelligence de cartographier correctement une requête en langage naturel. Mal typer un paramètre, c'est se garantir des dialogues bizarres avec Siri (croyez-moi sur parole).
Types primitifs supportés
String, Int, Double, Bool
Date, Measurement, Duration
URL, IntentFile (pour les fichiers)
CLPlacemark, CNContact (pour les lieux et contacts)
- iOS 26 :
Transcript, VisualIntelligenceMatch
Énumérations avec AppEnum
Pour offrir un menu de choix dans Raccourcis, conformez votre énumération à AppEnum :
enum Priorite: String, AppEnum {
case basse, moyenne, haute, urgente
static let typeDisplayRepresentation: TypeDisplayRepresentation =
"Priorité"
static let caseDisplayRepresentations: [Priorite: DisplayRepresentation] = [
.basse: "Basse",
.moyenne: "Moyenne",
.haute: "Haute",
.urgente: "Urgente"
]
}
Demande dynamique avec requestValue
Lorsqu'un paramètre est manquant à l'exécution, App Intents affiche automatiquement une boîte de dialogue. Mais vous pouvez aussi forcer la demande explicitement, ce qui est souvent plus propre :
func perform() async throws -> some IntentResult {
if titre.isEmpty {
titre = try await $titre.requestValue("Quel est le titre de la tâche ?")
}
// ...
return .result()
}
Exposer vos modèles avec AppEntity
Les types primitifs ne suffisent pas pour les vraies applications. Pour qu'un raccourci puisse manipuler vos objets (une tâche, un client, une note), il faut les exposer en tant qu'AppEntity. C'est un peu plus de boulot au départ, mais ça change tout côté UX.
struct TacheEntity: AppEntity {
let id: UUID
var titre: String
var estTerminee: Bool
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Tâche"
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(titre)",
subtitle: estTerminee ? "Terminée" : "À faire"
)
}
static let defaultQuery = TacheQuery()
}
struct TacheQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [TacheEntity] {
try await TacheStore.shared.taches(ids: identifiers)
}
func suggestedEntities() async throws -> [TacheEntity] {
try await TacheStore.shared.tachesRecentes(limite: 10)
}
}
Une EntityQuery indique au système comment retrouver vos entités à partir de leur identifiant. La méthode suggestedEntities() fournit la liste affichée par défaut dans Raccourcis (donc soignez-la, c'est la première chose que verra l'utilisateur). Pour la recherche par chaîne, ajoutez la conformité à EntityStringQuery et implémentez entities(matching:).
AppShortcutsProvider : exposer des phrases pour Siri
Pour qu'un utilisateur puisse dire « Dis Siri, ajoute une tâche dans MonApp », il faut déclarer une phrase d'invocation via AppShortcutsProvider.
struct MonAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: AjouterTacheIntent(),
phrases: [
"Ajouter une tâche dans \(.applicationName)",
"Nouvelle tâche dans \(.applicationName)",
"Crée une tâche dans \(.applicationName)"
],
shortTitle: "Ajouter une tâche",
systemImageName: "checklist"
)
}
}
Quelques règles à respecter :
- Chaque phrase doit contenir
\(.applicationName) au moins une fois (Apple est intransigeant là-dessus).
- Maximum 10
AppShortcut par application. Pas neuf, pas onze.
- Les phrases doivent être localisées via
AppShortcuts.strings dans chaque langue cible.
Apple Intelligence et les intents assistés (iOS 26)
iOS 26 introduit un nouveau type d'intent destiné à Apple Intelligence : les Assistant Schemas. Au lieu de définir vos propres titres et phrases, vous adoptez un schéma préétabli qu'Apple Intelligence comprend nativement (lecture, création, recherche, modification...). L'utilisateur peut alors interagir en langage naturel sans que vous ayez besoin d'anticiper toutes les formulations possibles. Un soulagement, franchement.
import AppIntents
@AssistantIntent(schema: .notes.create)
struct CreerNoteAssistee: AppIntent {
static let title: LocalizedStringResource = "Créer une note"
@Parameter(title: "Contenu")
var contenu: String
@Parameter(title: "Dossier", default: nil)
var dossier: DossierEntity?
func perform() async throws -> some IntentResult & ReturnsValue<NoteEntity> {
let note = try await NoteStore.shared.creer(
contenu: contenu,
dossier: dossier
)
return .result(value: note)
}
}
L'attribut @AssistantIntent garantit la signature attendue par Apple Intelligence. Si vous oubliez un paramètre obligatoire, le compilateur vous avertit immédiatement (et c'est tant mieux). Les schémas couvrent en 2026 plus de 100 cas d'usage répartis dans 18 domaines : notes, tâches, photos, courrier, navigation, et j'en passe.
Widgets interactifs et bouton Action
Les widgets iOS 17+ peuvent embarquer des boutons et des bascules qui déclenchent un AppIntent. La même structure d'intent peut être réutilisée sans modification — c'est exactement le genre de chose qui fait aimer cette API.
import WidgetKit
import SwiftUI
struct TacheWidgetView: View {
let tache: TacheEntity
var body: some View {
HStack {
Toggle(
isOn: tache.estTerminee,
intent: BasculerTacheIntent(tache: tache)
) {
Text(tache.titre)
}
.toggleStyle(.button)
}
}
}
Pour le bouton Action de l'iPhone 15 Pro et ultérieur, il suffit d'exposer un AppIntent simple : l'utilisateur peut le sélectionner directement dans Réglages > Bouton Action > Raccourci. C'est tout. Personnellement, j'ai branché « ajouter à ma liste de courses » sur mon bouton Action et je ne pourrais plus revenir en arrière.
Tester un AppIntent
Bonne nouvelle : les AppIntent sont des structures Swift ordinaires, donc entièrement testables avec Swift Testing ou XCTest. Aucun simulateur Siri n'est requis pour la logique métier.
import Testing
@testable import MonApp
@Test("Ajouter une tâche crée une entrée et retourne le bon dialogue")
func ajoutTacheReussi() async throws {
let intent = AjouterTacheIntent()
intent.titre = "Acheter du pain"
intent.echeance = Date().addingTimeInterval(3600)
let resultat = try await intent.perform()
#expect(TacheStore.shared.taches.count == 1)
#expect(TacheStore.shared.taches.first?.titre == "Acheter du pain")
}
Pour tester le parcours utilisateur complet (Siri, dialogue, désambiguïsation), utilisez App Intents Inspector dans Xcode 26 (menu Debug > App Intents). Il simule les requêtes en langage naturel et affiche le mapping vers vos intents en temps réel. Un outil que j'aurais payé cher en 2023.
Performance et bonnes pratiques
1. Privilégier l'exécution rapide
Apple Intelligence et Siri imposent une limite implicite de 10 secondes pour l'exécution d'un intent. Au-delà, l'utilisateur reçoit un message d'erreur — pas génial pour l'expérience. Pour les opérations longues, retournez immédiatement un résultat et utilisez BackgroundTasks ou une notification de mise à jour.
2. Utiliser openAppWhenRun avec parcimonie
La propriété static let openAppWhenRun = true force l'ouverture de votre application. Ça casse toute l'expérience mains-libres de Siri et CarPlay. Réservez-la aux intents qui exigent vraiment l'interface (édition complexe, sélection média, ce genre de choses).
3. Localiser les phrases et descriptions
Toutes les chaînes utilisateur doivent passer par LocalizedStringResource. Sinon, Siri ne déclenchera tout simplement pas votre intent dans une autre langue. Utilisez String Catalogs (Xcode 15+) pour gérer les variantes facilement — c'est nettement plus agréable que les anciens .strings.
4. Pas de SwiftUI dans perform()
La méthode perform() s'exécute hors du thread principal et hors d'un cycle SwiftUI. N'y appelez pas d'API liées à @Environment ou à la hiérarchie des vues. Pour ouvrir une vue spécifique, retournez OpensIntent et gérez la navigation dans onAppear.
5. Sécurité des données
Un intent peut être exécuté lorsque l'appareil est verrouillé (selon authenticationPolicy). N'exposez pas d'opérations sensibles sans déclarer :
static let authenticationPolicy: IntentAuthenticationPolicy = .requiresAuthentication
Migrer depuis SiriKit ou Intents.framework
SiriKit reste pris en charge en 2026, mais Apple a marqué INIntent comme déprécié dès iOS 18 et ne corrige plus les nouvelles fonctionnalités. La migration est généralement directe :
- Identifiez chaque
INIntent existant dans votre fichier .intentdefinition.
- Créez une nouvelle structure
AppIntent dans Swift avec les mêmes paramètres.
- Réimplémentez la logique de
handle() dans perform().
- Supprimez l'extension Intents et le fichier
.intentdefinition une fois la couverture validée.
Profitez-en pour adopter au passage les Assistant Schemas correspondants : la transition vers Apple Intelligence sera immédiate.
Erreurs fréquentes et solutions
L'intent n'apparaît pas dans Raccourcis
- Vérifiez que la structure est compilée dans la cible app (et non uniquement dans une extension). C'est l'erreur n°1, à vue de nez.
- Forcez la régénération en exécutant l'application au moins une fois sur le simulateur ou l'appareil.
- Si vous utilisez SwiftPM, exposez l'intent via
@_exported import dans la cible app.
Siri répond « Je ne peux pas faire ça avec MonApp »
- La phrase doit obligatoirement contenir
\(.applicationName).
- Allez dans Réglages > Siri et Recherche > MonApp et activez « Apprendre depuis cette app ».
- Sur le simulateur, basculez la langue Siri pour qu'elle corresponde à votre
String Catalog. Bug pénible mais classique.
Apple Intelligence ne déclenche pas l'intent
- Apple Intelligence ne route que vers les
@AssistantIntent conformes à un schéma reconnu.
- Vérifiez que l'utilisateur a bien activé Apple Intelligence dans Réglages.
- L'appareil doit être un iPhone 15 Pro ou ultérieur, ou un iPad/Mac compatible.
FAQ
Quelle est la différence entre App Intents et SiriKit ?
SiriKit s'appuie sur des domaines prédéfinis et des fichiers .intentdefinition compilés. App Intents, c'est une API Swift moderne, sans fichier de configuration, qui supporte n'importe quelle action de votre app, plus l'intégration avec Spotlight, les widgets, le bouton Action et Apple Intelligence. Apple recommande App Intents pour tout nouveau développement depuis iOS 16 — pas de raison d'aller à contre-courant.
App Intents fonctionne-t-il sur iOS 16 et iOS 17 ?
Oui. App Intents est disponible depuis iOS 16. Les fonctionnalités avancées (widgets interactifs, bouton Action, Apple Intelligence, Assistant Schemas) sont arrivées progressivement avec iOS 17, 18 et 26. Utilisez @available pour gérer la rétrocompatibilité proprement.
Combien d'AppShortcut puis-je déclarer ?
Apple limite à 10 AppShortcut par application. C'est volontaire : Apple Intelligence et Siri privilégient les phrases directes et claires plutôt qu'une longue liste de commandes. Pour plus d'actions, exposez-les comme AppIntent sans phrase invocable directement (ils restent disponibles dans Raccourcis, ne vous inquiétez pas).
Puis-je utiliser Foundation Models à l'intérieur d'un AppIntent ?
Oui, et c'est même un schéma assez puissant. Un AppIntent peut appeler un modèle on-device pour générer du texte, classer un input ou extraire des entités, puis retourner le résultat à Siri ou Apple Intelligence. Attention quand même à respecter la limite de 10 secondes ; pour les générations longues, retournez un placeholder et complétez en arrière-plan.
Comment déboguer un AppIntent qui plante silencieusement ?
Activez le scheme « App Intents » dans Xcode 26 et lancez Debug > App Intents > Inspector. Vous verrez le routing en temps réel, les erreurs de paramètres et les valeurs retournées. Pour la production, jetez les logs via Logger de os.log sur le subsystem com.apple.appintents.
Conclusion
App Intents n'est plus une option en 2026 : c'est le canal officiel par lequel votre application est découverte et utilisée hors de son interface. Quelques heures d'investissement suffisent pour exposer les actions clés à Siri, Spotlight et Apple Intelligence — ce qui multiplie la surface d'usage de votre app sans vraiment alourdir votre code.
Mon conseil ? Commencez petit, avec un ou deux AppIntent sur les actions les plus utiles. Ajoutez les AppEntity correspondantes, puis adoptez les Assistant Schemas au fur et à mesure que vos cas d'usage matchent les schémas Apple. Vous serez surpris de voir à quelle vitesse ça paie.