App Intents 2.0 et Siri Shortcuts : guide automatisation iOS 27

Guide complet App Intents 2.0 et Siri Shortcuts pour iOS 27. Créer des streaming responses, conversations multi-tours, View Annotations et intégrer Foundation Models.

App Intents et Siri Shortcuts pour automatisation iOS avancée avec Swift et Apple Intelligence

App Intents 2.0 et Siri Shortcuts représentent la seule voie permettant aux applications tierces de s'intégrer avec Siri sur iOS 27. Suite à la WWDC 2026, Apple a déprécié SiriKit et positionné App Intents comme le framework obligatoire pour l'interaction vocale, la découverte Spotlight et les workflows d'automatisation.

Ce que couvre cet article

Cet article présente la création complète d'App Intents et Siri Shortcuts pour iOS 27, depuis les concepts fondamentaux jusqu'aux streaming responses, conversations multi-tours et l'API View Annotations.

Comprendre le framework App Intents 2.0

Le framework App Intents, introduit avec iOS 16 et étendu à la version 2.0 lors de la WWDC 2026, est un framework Swift-natif et déclaratif pour construire des actions découvrables par le système. iOS 27 apporte quatre ajouts majeurs : les streaming responses pour les opérations longues, les follow-ups conversationnels multi-tours, les View Annotations pour référencer des éléments à l'écran, et les App Schemas pour la compréhension sémantique sans phrases d'entraînement.

Lors de la WWDC 2026, Apple a formellement déprécié SiriKit et fait d'App Intents la seule façon dont Siri peut interagir avec les applications tierces. Le code SiriKit existant continue de compiler avec des avertissements de dépréciation, mais Apple a signalé une fenêtre de support de deux à trois ans avant suppression.

TaskIntent.swiftswift
import AppIntents

// An AppIntent represents an action users can perform
struct CreateTaskIntent: AppIntent {
    // Title displayed in Shortcuts and Siri
    static var title: LocalizedStringResource = "Create a task"

    // Description for accessibility and suggestions
    static var description = IntentDescription(
        "Creates a new task in the application."
    )

    // Parameter with automatic validation
    @Parameter(title: "Task title")
    var taskTitle: String

    // Optional parameter with default value
    @Parameter(title: "Priority", default: .medium)
    var priority: TaskPriority

    // Action execution with async/await
    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        // Create task via service
        let task = TaskService.shared.createTask(
            title: taskTitle,
            priority: priority
        )

        // Return created entity for chaining
        return .result(value: TaskEntity(task: task))
    }
}

L'intent déclare ses paramètres avec le property wrapper @Parameter, permettant à Siri de demander les valeurs manquantes de façon conversationnelle. La méthode perform() exécute la logique métier et retourne un résultat typé. Le système de types lui-même sert de schema que Siri AI découvre dynamiquement.

Streaming Responses pour actions longues

App Intents 2.0 introduit les streaming responses, permettant aux intents de rapporter leur progression pendant l'exécution plutôt que de bloquer jusqu'à complétion. Cette capacité adresse les scénarios comme l'upload de fichiers, la synchronisation de données ou les calculs complexes où l'utilisateur a besoin de feedback.

StreamingExportIntent.swiftswift
import AppIntents

struct ExportDataIntent: AppIntent {
    static var title: LocalizedStringResource = "Export data"

    @Parameter(title: "Format")
    var format: ExportFormat

    // Streaming response for progress reporting
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let totalItems = DataService.shared.itemCount
        var processed = 0

        // Stream progress updates to Siri
        for item in DataService.shared.allItems {
            await exportItem(item, format: format)
            processed += 1

            // Report progress at intervals
            if processed % 100 == 0 {
                await reportProgress(
                    "Exported (processed) of (totalItems) items..."
                )
            }
        }

        return .result(
            dialog: "Export complete. (totalItems) items saved as (format.rawValue)."
        )
    }
}

Les streaming responses permettent un feedback naturel pendant les opérations qui prennent plus de quelques secondes, maintenant l'engagement de l'utilisateur au lieu de le laisser attendre en silence.

Follow-ups conversationnels multi-tours

Les conversations multi-tours permettent à Siri de poser des questions clarificatrices et de continuer l'interaction dans une seule session. Cette fonctionnalité transforme les App Intents de commandes one-shot en workflows conversationnels.

ConversationalTaskIntent.swiftswift
import AppIntents

struct SmartTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create smart task"

    @Parameter(title: "Task title")
    var taskTitle: String?

    @Parameter(title: "Priority")
    var priority: TaskPriority?

    @Parameter(title: "Due date")
    var dueDate: Date?

    // Multi-turn conversation flow
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Request missing parameters conversationally
        let title = try await taskTitle ?? requestValue(
            for: .$taskTitle,
            dialog: "What should the task be called?"
        )

        let taskPriority = try await priority ?? requestValue(
            for: .$priority,
            dialog: "What priority level?"
        )

        // Optional follow-up
        let date: Date?
        if try await requestConfirmation(
            result: .result(dialog: "Should this task have a due date?")
        ) {
            date = try await requestValue(
                for: .$dueDate,
                dialog: "When is it due?"
            )
        } else {
            date = nil
        }

        let task = TaskService.shared.createTask(
            title: title,
            priority: taskPriority,
            dueDate: date
        )

        return .result(
            dialog: "Created '(task.title)' with (taskPriority.rawValue) priority."
        )
    }
}

La méthode requestValue(for:dialog:) suspend l'exécution jusqu'à ce que Siri reçoive la réponse de l'utilisateur, créant un échange naturel d'aller-retour plutôt que d'exiger tous les paramètres d'avance.

API View Annotations pour références à l'écran

L'API View Annotations, nouvelle dans iOS 27, permet aux utilisateurs de référencer des éléments UI directement dans les commandes Siri avec des phrases comme "cette photo", "le troisième" ou "ce message". Cette capacité nécessite d'annoter les vues SwiftUI avec des informations sémantiques.

AnnotatedPhotoView.swiftswift
import SwiftUI
import AppIntents

struct PhotoGridView: View {
    let photos: [Photo]

    var body: some View {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
            ForEach(photos) { photo in
                PhotoThumbnail(photo: photo)
                    // Annotate view for Siri reference
                    .appIntentAnnotation(
                        entity: PhotoEntity(photo: photo),
                        label: photo.title
                    )
            }
        }
    }
}

// Intent that accepts view-referenced entities
struct SharePhotoIntent: AppIntent {
    static var title: LocalizedStringResource = "Share photo"

    // Parameter resolved from on-screen annotation
    @Parameter(title: "Photo", supportsViewAnnotation: true)
    var photo: PhotoEntity

    func perform() async throws -> some IntentResult & ProvidesDialog {
        await ShareService.share(photo.id)
        return .result(dialog: "Photo shared.")
    }
}

Quand un utilisateur dit "partage cette photo" en regardant la grille, Siri résout "cette" vers la PhotoEntity actuellement focalisée ou touchée en dernier via l'annotation. Cela supprime la friction de décrire les éléments verbalement.

Compatibilité des appareils

View Annotations nécessite iOS 27, mais la couche App Intents elle-même fonctionne sur tous les appareils iOS 27 y compris iPhone 11 et ultérieurs. Les fonctionnalités Apple Intelligence comme la conscience d'écran nécessitent iPhone 15 Pro ou ultérieur.

Définir des App Entities pour les données

Les App Entities représentent les "noms" de l'application : les objets sur lesquels les intents opèrent. Elles permettent à Siri de comprendre, rechercher et manipuler les données applicatives. Construire des entités efficaces est essentiel pour les patterns de gestion d'état SwiftUI dans les apps orientées intents.

TaskEntity.swiftswift
import AppIntents

// Internal data model
struct Task: Identifiable, Codable {
    let id: UUID
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool
    var dueDate: Date?
}

// Entity exposed to the system
struct TaskEntity: AppEntity {
    // Required unique identifier
    var id: UUID

    // Displayable properties
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool

    // Display configuration in the system
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Task"

    // Visual representation of the instance
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "(title)",
            subtitle: "(priority.rawValue)",
            image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
        )
    }

    // Default query for searching entities
    static var defaultQuery = TaskEntityQuery()

    // Initializer from internal model
    init(task: Task) {
        self.id = task.id
        self.title = task.title
        self.priority = task.priority
        self.isCompleted = task.isCompleted
    }
}

// Optimized entity query with search
struct TaskEntityQuery: EntityStringQuery {
    // Text search with service-side filtering
    func entities(matching string: String) async throws -> [TaskEntity] {
        TaskService.shared.search(query: string, limit: 10)
            .map { TaskEntity(task: $0) }
    }

    // Search by identifiers
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        TaskService.shared.fetchTasks()
            .filter { identifiers.contains($0.id) }
            .map { TaskEntity(task: $0) }
    }

    // Limited suggestions for performance
    func suggestedEntities() async throws -> [TaskEntity] {
        TaskService.shared.fetchRecentTasks(limit: 5)
            .map { TaskEntity(task: $0) }
    }
}

Le protocole EntityStringQuery ajoute la capacité de recherche textuelle, permettant à Siri de trouver des entités par nom. La méthode suggestedEntities() alimente les interfaces Siri et Shortcuts avec des options pertinentes.

Utiliser AppEnum pour valeurs fixes

Utilisez AppEnum pour les types avec un ensemble fixe de valeurs (priorité, statut), et AppEntity pour les types dynamiques créés par l'utilisateur (tâches, notes, contacts). Les mélanger cause une complexité inutile.

Créer des App Enums pour les valeurs fixes

Les App Enums exposent des types énumérés au système, permettant à Siri de proposer des choix contextuels avec des représentations visuelles.

TaskPriority.swiftswift
import AppIntents

// Enum exposed to the system
enum TaskPriority: String, AppEnum, Codable {
    case low
    case medium
    case high

    // Type name displayed
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priority"

    // Representation of each case
    static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
        .low: DisplayRepresentation(
            title: "Low",
            image: .init(systemName: "arrow.down.circle")
        ),
        .medium: DisplayRepresentation(
            title: "Medium",
            image: .init(systemName: "minus.circle")
        ),
        .high: DisplayRepresentation(
            title: "High",
            image: .init(systemName: "exclamationmark.circle")
        )
    ]
}

Les icônes SF Symbols enrichissent l'affichage dans Shortcuts et les suggestions Siri, rendant la sélection plus rapide que les descriptions verbales.

Prêt à réussir tes entretiens iOS ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Implémenter l'AppShortcutsProvider

L'AppShortcutsProvider expose les App Shortcuts au système, les rendant immédiatement disponibles sans configuration utilisateur. Ces raccourcis apparaissent dans Spotlight, Siri, l'Action Button et les nouveaux workflows agentiques de Siri AI.

ShortcutsProvider.swiftswift
import AppIntents

// Provider declaring all app shortcuts
struct TaskAppShortcutsProvider: AppShortcutsProvider {
    // Maximum 10 shortcuts per application
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Shortcut to create a task
        AppShortcut(
            intent: CreateTaskIntent(),
            phrases: [
                // The .applicationName placeholder is REQUIRED
                "Create a task with (.applicationName)",
                "New task in (.applicationName)",
                "Add a task to (.applicationName)"
            ],
            shortTitle: "Create Task",
            systemImageName: "plus.circle"
        )

        // Shortcut with dynamic parameter
        AppShortcut(
            intent: CompleteTaskIntent(),
            phrases: [
                "Complete (.$taskName) in (.applicationName)",
                "Mark (.$taskName) as done with (.applicationName)"
            ],
            shortTitle: "Complete Task",
            systemImageName: "checkmark.circle"
        )

        // Shortcut for smart conversational intent
        AppShortcut(
            intent: SmartTaskIntent(),
            phrases: [
                "Quick task (.applicationName)",
                "Remind me (.applicationName)"
            ],
            shortTitle: "Smart Task",
            systemImageName: "brain"
        )
    }
}

Les phrases vocales doivent inclure le placeholder (.applicationName) pour que Siri identifie l'application cible. Les paramètres dynamiques comme (.$taskName) permettent des commandes contextuelles.

Intents dans les widgets interactifs

Les App Intents s'intègrent avec WidgetKit pour créer des widgets interactifs. Comprendre les patterns de navigation SwiftUI aide à construire les transitions widget-vers-app.

TaskWidgetIntents.swiftswift
import AppIntents
import WidgetKit

// Widget-optimized intent (fast execution)
struct ToggleTaskFromWidgetIntent: AppIntent {
    static var title: LocalizedStringResource = "Toggle task"

    @Parameter(title: "Task ID")
    var taskID: String

    init() {}

    init(taskID: UUID) {
        self.taskID = taskID.uuidString
    }

    // No dialog for widgets
    func perform() async throws -> some IntentResult {
        guard let uuid = UUID(uuidString: taskID) else {
            return .result()
        }

        TaskService.shared.toggleCompletion(taskId: uuid)

        // Immediate widget refresh
        WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")

        return .result()
    }
}

// Widget view with interactive button
import SwiftUI

struct TaskWidgetView: View {
    let task: Task

    var body: some View {
        Button(intent: ToggleTaskFromWidgetIntent(taskID: task.id)) {
            HStack {
                Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                    .foregroundStyle(task.isCompleted ? .green : .secondary)

                Text(task.title)
                    .strikethrough(task.isCompleted)
            }
            .padding()
        }
        .buttonStyle(.plain)
    }
}

Les widgets utilisent la syntaxe Button(intent:) pour connecter l'interaction directement à l'App Intent sans ouvrir l'application.

Intégration avec Foundation Models Framework

iOS 27 introduit le Foundation Models framework, permettant l'inférence LLM on-device. Bien que Foundation Models n'ait pas d'intégration directe avec Siri, les apps peuvent l'utiliser dans les App Intents pour traiter le langage naturel ou générer des réponses.

AIAssistedIntent.swiftswift
import AppIntents
import FoundationModels

struct SummarizeNotesIntent: AppIntent {
    static var title: LocalizedStringResource = "Summarize notes"

    @Parameter(title: "Topic")
    var topic: String

    func perform() async throws -> some IntentResult & ProvidesDialog {
        let notes = NoteService.shared.fetchNotes(matching: topic)
        let content = notes.map(.content).joined(separator: "

")

        // Use Foundation Models for on-device summarization
        let model = LanguageModel.default
        let summary = try await model.generate(
            prompt: "Summarize these notes in 2 sentences: (content)",
            maxTokens: 100
        )

        return .result(dialog: IntentDialog(summary))
    }
}

Foundation Models s'exécute à environ 30 tokens par seconde on-device sans coût par requête. Le framework nécessite iPhone 15 Pro ou iPhone 16 et ultérieur pour le traitement on-device.

Migration de SiriKit vers App Intents

Pour les apps utilisant encore SiriKit, la migration suit un processus structuré. Xcode 27 fournit un outil "Convert to App Intent" pour les configurations Widget, mais les handlers personnalisés nécessitent une réécriture manuelle.

swift
// Before: SiriKit Intent Handler
class CreateTaskIntentHandler: NSObject, CreateTaskIntentHandling {
    func handle(intent: CreateTaskIntent) async -> CreateTaskIntentResponse {
        guard let title = intent.taskTitle else {
            return CreateTaskIntentResponse(code: .failure, userActivity: nil)
        }

        let task = TaskService.shared.createTask(title: title)
        return CreateTaskIntentResponse.success(task: task)
    }
}

// After: App Intent (iOS 27)
struct CreateTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create a task"

    @Parameter(title: "Task title")
    var taskTitle: String

    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        let task = TaskService.shared.createTask(title: taskTitle)
        return .result(value: TaskEntity(task: task))
    }
}

La migration élimine les fichiers .intentdefinition et les targets Intent Extension. Toute la logique d'intents se déplace dans le target principal de l'app sous forme de structs Swift.

Tests des App Intents

Le framework AppIntentsTesting valide les intents via l'infrastructure réelle de Siri, Shortcuts et Spotlight sans mocks.

TaskIntentTests.swiftswift
import XCTest
import AppIntentsTesting
@testable import TaskApp

final class TaskIntentTests: XCTestCase {

    override func setUp() {
        super.setUp()
        TaskService.shared.reset()
    }

    func testCreateTaskIntent() async throws {
        // Given
        var intent = CreateTaskIntent()
        intent.taskTitle = "Test Task"
        intent.priority = .high

        // When
        let result = try await intent.perform()

        // Then
        let tasks = TaskService.shared.fetchTasks()
        XCTAssertEqual(tasks.count, 1)
        XCTAssertEqual(tasks.first?.title, "Test Task")
        XCTAssertEqual(tasks.first?.priority, .high)
    }

    func testEntityQuery() async throws {
        // Given
        let task1 = TaskService.shared.createTask(title: "Task 1", priority: .low)
        let task2 = TaskService.shared.createTask(title: "Task 2", priority: .high)

        let query = TaskEntityQuery()

        // When
        let entities = try await query.entities(for: [task1.id, task2.id])

        // Then
        XCTAssertEqual(entities.count, 2)
    }

    func testStreamingIntent() async throws {
        // Given
        var intent = ExportDataIntent()
        intent.format = .json

        // When
        let result = try await intent.perform()

        // Then: verify completion dialog
        XCTAssertNotNil(result)
    }
}

Les tests vérifient le comportement des intents indépendamment de l'interface système, détectant les régressions avant déploiement.

Sources

Checklist d'implémentation App Intents iOS 27

Pour les apps ciblant iOS 27, les App Intents ne sont plus optionnels. Siri AI compose des actions multi-étapes à travers les apps, et les apps sans intents publiés sont exclues de ces workflows agentiques.

  • Créer des AppIntents pour les actions principales de l'application
  • Définir des AppEntities pour les données manipulables
  • Utiliser AppEnum pour les types énumérés
  • Implémenter AppShortcutsProvider avec phrases vocales
  • Respecter la limite de 10 App Shortcuts maximum
  • Inclure (.applicationName) dans toutes les phrases
  • Ajouter des View Annotations aux vues SwiftUI pour références à l'écran
  • Implémenter les streaming responses pour opérations longues
  • Supporter les conversations multi-tours pour workflows complexes
  • Migrer depuis SiriKit avant la fermeture de la fenêtre de dépréciation
  • Tester les intents avec le framework AppIntentsTesting
  • Localiser les titres et descriptions

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Défi du jour

Tu saurais repérer le bug en iOS ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur de SharpSkill

Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.

Mis à jour le 20 août 2026

Tags

#app-intents
#siri-shortcuts
#ios
#swift
#apple-intelligence

Partager

Articles similaires