App Intents 2.0 e Siri Shortcuts: guida all'automazione iOS 27

Guida completa ad App Intents 2.0 e Siri Shortcuts per iOS 27. Risposte streaming, conversazioni multi-turno, View Annotations e integrazione Foundation Models.

App Intents e Siri Shortcuts per automazione iOS avanzata con Swift e Apple Intelligence

App Intents 2.0 e Siri Shortcuts rappresentano l'unico percorso per le app di terze parti per integrarsi con Siri su iOS 27. Dopo la WWDC 2026, Apple ha deprecato SiriKit e posizionato App Intents come framework obbligatorio per l'interazione vocale, la scoperta in Spotlight e i workflow di automazione.

Cosa copre questo articolo

Questo articolo presenta la creazione completa di App Intents e Siri Shortcuts per iOS 27, dai concetti fondamentali alle risposte in streaming, alle conversazioni multi-turno e all'API View Annotations.

Comprendere il framework App Intents 2.0

Il framework App Intents, introdotto con iOS 16 ed espanso alla versione 2.0 alla WWDC 2026, è un framework Swift-nativo e dichiarativo per la creazione di azioni rilevabili dal sistema. iOS 27 porta quattro aggiunte principali: risposte in streaming per operazioni di lunga durata, follow-up conversazionali multi-turno, View Annotations per referenziare elementi a schermo e App Schemas per la comprensione semantica senza frasi di addestramento.

Alla WWDC 2026, Apple ha formalmente deprecato SiriKit e reso App Intents l'unico modo con cui Siri può interagire con app di terze parti. Il codice SiriKit esistente continua a compilare con warning di deprecazione, ma Apple ha segnalato una finestra di supporto di due-tre anni prima della rimozione.

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 dichiara i parametri usando il property wrapper @Parameter, permettendo a Siri di richiedere i valori mancanti in modo conversazionale. Il metodo perform() esegue la logica di business e restituisce un risultato tipizzato. Il sistema dei tipi stesso funge da schema che Siri AI scopre dinamicamente.

Risposte in streaming per azioni di lunga durata

App Intents 2.0 introduce le risposte in streaming, permettendo agli intents di riportare il progresso durante l'esecuzione invece di bloccare fino al completamento. Questa capacità affronta scenari come upload di file, sincronizzazione dati o calcoli complessi dove gli utenti necessitano 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)."
        )
    }
}

Le risposte in streaming permettono feedback naturale durante operazioni che richiedono più di qualche secondo, mantenendo l'utente coinvolto invece di lasciarlo in attesa silenziosa.

Conversazioni multi-turno

Le conversazioni multi-turno permettono a Siri di porre domande di chiarimento e continuare l'interazione all'interno di una singola sessione. Questa funzionalità trasforma App Intents da comandi singoli a workflow conversazionali.

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: .,
            dialog: "What should the task be called?"
        )

        let taskPriority = try await priority ?? requestValue(
            for: .,
            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: .,
                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."
        )
    }
}

Il metodo requestValue(for:dialog:) mette in pausa l'esecuzione finché Siri non riceve la risposta dell'utente, creando uno scambio naturale botta e risposta invece di richiedere tutti i parametri in anticipo.

API View Annotations per riferimenti a schermo

L'API View Annotations, nuova in iOS 27, permette agli utenti di referenziare elementi UI direttamente nei comandi Siri usando frasi come "questa foto", "la terza" o "quel messaggio". Questa capacità richiede l'annotazione delle view SwiftUI con informazioni semantiche.

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

Quando un utente dice "condividi questa foto" mentre visualizza la griglia, Siri risolve "questa" alla PhotoEntity attualmente focalizzata o toccata per ultima attraverso l'annotazione. Questo elimina l'attrito di dover descrivere gli elementi verbalmente.

Compatibilità dispositivi

View Annotations richiede iOS 27, ma il layer App Intents funziona su ogni dispositivo iOS 27 incluso iPhone 11 e successivi. Le funzionalità Apple Intelligence come la consapevolezza a schermo richiedono iPhone 15 Pro o successivi.

Definire App Entities per i dati

Le App Entities rappresentano i "sostantivi" dell'applicazione: gli oggetti su cui operano gli intents. Permettono a Siri di comprendere, cercare e manipolare i dati dell'app. Costruire entities efficaci è essenziale per i pattern di SwiftUI state management nelle app intent-driven.

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: /bin/bash) }
    }

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

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

Il protocollo EntityStringQuery aggiunge la capacità di ricerca testuale, permettendo a Siri di trovare entities per nome. Il metodo suggestedEntities() alimenta le interfacce di Siri e Comandi rapidi con opzioni rilevanti.

Usare AppEnum per valori fissi

Usare AppEnum per tipi con un insieme fisso di valori (priorità, stato), e AppEntity per tipi dinamici creati dall'utente (attività, note, contatti). Mescolarli causa complessità non necessaria.

Creare App Enums per valori fissi

Gli App Enums espongono tipi enumerati al sistema, permettendo a Siri di offrire scelte contestuali con rappresentazioni visive.

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

Le icone SF Symbols arricchiscono la visualizzazione nei Comandi rapidi e nei suggerimenti di Siri, rendendo la selezione più veloce delle descrizioni verbali.

Pronto a superare i tuoi colloqui su iOS?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Implementare AppShortcutsProvider

L'AppShortcutsProvider espone gli App Shortcuts al sistema, rendendoli immediatamente disponibili senza configurazione utente. Questi shortcuts appaiono in Spotlight, Siri, l'Action Button e i nuovi workflow agentici di 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 (.) in (.applicationName)",
                "Mark (.) 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"
        )
    }
}

Le frasi vocali devono includere il placeholder (.applicationName) affinché Siri identifichi l'app di destinazione. I parametri dinamici come (.) abilitano comandi contestuali.

Intents nei widget interattivi

Gli App Intents si integrano con WidgetKit per creare widget interattivi. Comprendere i pattern di navigazione SwiftUI aiuta nella costruzione delle transizioni widget-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)
    }
}

I widget usano la sintassi Button(intent:) per collegare l'interazione direttamente all'App Intent senza aprire l'applicazione.

Integrazione Foundation Models Framework

iOS 27 introduce il Foundation Models framework, che abilita l'inferenza LLM on-device. Mentre Foundation Models non ha integrazione diretta con Siri, le app possono usarlo all'interno degli App Intents per elaborare linguaggio naturale o generare risposte.

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 gira a circa 30 token al secondo on-device senza costi per richiesta. Il framework richiede iPhone 15 Pro o iPhone 16 e successivi per l'elaborazione on-device.

Migrazione da SiriKit ad App Intents

Per le app che ancora usano SiriKit, la migrazione segue un processo strutturato. Xcode 27 fornisce uno strumento "Convert to App Intent" per le configurazioni Widget, ma gli handler personalizzati richiedono riscrittura manuale.

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 migrazione elimina i file .intentdefinition e i target Intent Extension. Tutta la logica degli intent si sposta nel target principale dell'app come struct Swift.

Testare gli App Intents

Il framework AppIntentsTesting valida gli intents attraverso l'infrastruttura reale di Siri, Comandi rapidi e Spotlight senza mock.

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

I test verificano il comportamento degli intents indipendentemente dall'interfaccia di sistema, catturando regressioni prima del deployment.

Sources

Checklist implementazione App Intents iOS 27

Per le app che puntano a iOS 27, App Intents non sono più opzionali. Siri AI compone azioni multi-step attraverso le app, e le app senza intents pubblicati vengono escluse da questi workflow agentici.

  • Creare AppIntents per le azioni principali dell'app
  • Definire AppEntities per i dati manipolabili
  • Usare AppEnum per i tipi enumerati
  • Implementare AppShortcutsProvider con frasi vocali
  • Rispettare il limite massimo di 10 App Shortcuts
  • Includere (.applicationName) in tutte le frasi
  • Aggiungere View Annotations alle view SwiftUI per riferimenti a schermo
  • Implementare risposte streaming per operazioni di lunga durata
  • Supportare conversazioni multi-turno per workflow complessi
  • Migrare da SiriKit prima della chiusura della finestra di deprecazione
  • Testare gli intents con il framework AppIntentsTesting
  • Localizzare titoli e descrizioni

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Sfida del giorno

Sapresti trovare il bug in iOS?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 20 agosto 2026

Tag

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

Condividi

Articoli correlati