App Intents e Siri Shortcuts: automazione iOS avanzata 2026
Guida completa ad App Intents e Siri Shortcuts per iOS 18+. Creare azioni personalizzate per Siri, integrare Apple Intelligence e automatizzare l'app Swift nel 2026.

Nel 2026, con Apple Intelligence e il framework App Intents, le applicazioni iOS entrano in un'era in cui l'intento dell'utente conta più dell'interfaccia grafica. Le app che non espongono intents diventano invisibili in un sistema operativo orientato all'IA. App Intents costituisce la fondazione che permette a Siri, Spotlight, ai widget e all'Action Button di interagire con le funzionalità dell'app.
Questo articolo presenta la creazione completa di App Intents e Siri Shortcuts per iOS 18+, dai concetti fondamentali fino all'integrazione con Apple Intelligence e gli App Intent Domains.
Comprendere il framework App Intents
Il framework App Intents, introdotto con iOS 16, modernizza la costruzione degli intents in Swift sostituendo il vecchio framework SiriKit Intents. Questa architettura dichiarativa consente di creare azioni rilevabili dal sistema: Spotlight, app Comandi rapidi, Siri e Action Button.
import AppIntents
// Un AppIntent rappresenta un'azione che l'utente può eseguire
struct CreateTaskIntent: AppIntent {
// Titolo mostrato in Comandi rapidi e Siri
static var title: LocalizedStringResource = "Crea un'attività"
// Descrizione per accessibilità e suggerimenti
static var description = IntentDescription(
"Crea una nuova attività nell'applicazione."
)
// Parametro con validazione automatica
@Parameter(title: "Titolo dell'attività")
var taskTitle: String
// Parametro opzionale con valore di default
@Parameter(title: "Priorità", default: .medium)
var priority: TaskPriority
// Esecuzione dell'azione
func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
// Crea l'attività tramite il service
let task = TaskService.shared.createTask(
title: taskTitle,
priority: priority
)
// Restituisce l'entità creata per il concatenamento
return .result(value: TaskEntity(task: task))
}
}L'intent dichiara i suoi parametri tramite il property wrapper @Parameter, permettendo a Siri di richiedere i valori mancanti. Il metodo perform() esegue la logica di business e restituisce un risultato tipizzato.
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 e manipolare i dati dell'app.
import AppIntents
// Modello dati interno
struct Task: Identifiable, Codable {
let id: UUID
var title: String
var priority: TaskPriority
var isCompleted: Bool
var dueDate: Date?
}
// Entità esposta al sistema
struct TaskEntity: AppEntity {
// Identificatore univoco richiesto
var id: UUID
// Proprietà visualizzabili
var title: String
var priority: TaskPriority
var isCompleted: Bool
// Configurazione di visualizzazione nel sistema
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Attività"
// Rappresentazione visiva dell'istanza
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(title)",
subtitle: "\(priority.rawValue)",
image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
)
}
// Query di default per la ricerca delle entità
static var defaultQuery = TaskEntityQuery()
// Initializer dal modello interno
init(task: Task) {
self.id = task.id
self.title = task.title
self.priority = task.priority
self.isCompleted = task.isCompleted
}
}
// Query per ricercare e filtrare le entità
struct TaskEntityQuery: EntityQuery {
// Ricerca per identificatori
func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
TaskService.shared.fetchTasks()
.filter { identifiers.contains($0.id) }
.map { TaskEntity(task: $0) }
}
// Suggerimenti mostrati nell'interfaccia
func suggestedEntities() async throws -> [TaskEntity] {
TaskService.shared.fetchTasks()
.filter { !$0.isCompleted }
.prefix(5)
.map { TaskEntity(task: $0) }
}
}L'EntityQuery definisce come il sistema cerca e suggerisce le entità. I metodi entities(for:) e suggestedEntities() alimentano le interfacce di Siri e dei Comandi rapidi.
Usare AppEnum per tipi con un insieme fisso di valori (priorità, stato), e AppEntity per tipi dinamici creati dall'utente (attività, note, contatti).
Creare App Enums per valori fissi
Gli App Enums espongono tipi enumerati al sistema, permettendo a Siri di offrire scelte contestuali.
import AppIntents
// Enum esposto al sistema
enum TaskPriority: String, AppEnum, Codable {
case low
case medium
case high
// Nome del tipo visualizzato
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priorità"
// Rappresentazione di ogni caso
static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
.low: DisplayRepresentation(
title: "Bassa",
image: .init(systemName: "arrow.down.circle")
),
.medium: DisplayRepresentation(
title: "Media",
image: .init(systemName: "minus.circle")
),
.high: DisplayRepresentation(
title: "Alta",
image: .init(systemName: "exclamationmark.circle")
)
]
}
// Enum per lo stato dell'attività
enum TaskStatus: String, AppEnum, Codable {
case pending
case inProgress
case completed
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Stato"
static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [
.pending: "In attesa",
.inProgress: "In corso",
.completed: "Completata"
]
}Le rappresentazioni visive (icone SF Symbols) arricchiscono la visualizzazione nei Comandi rapidi e nei suggerimenti di Siri.
Implementare AppShortcutsProvider
L'AppShortcutsProvider espone gli App Shortcuts al sistema, rendendoli immediatamente disponibili senza configurazione utente. Questi shortcut compaiono in Spotlight, Siri e Action Button.
import AppIntents
// Provider che dichiara tutti gli shortcut dell'app
struct TaskAppShortcutsProvider: AppShortcutsProvider {
// Massimo 10 shortcut per applicazione
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
// Shortcut per creare un'attività
AppShortcut(
intent: CreateTaskIntent(),
phrases: [
// Il placeholder .applicationName è OBBLIGATORIO
"Crea un'attività con \(.applicationName)",
"Nuova attività in \(.applicationName)",
"Aggiungi un'attività a \(.applicationName)"
],
shortTitle: "Crea attività",
systemImageName: "plus.circle"
)
// Shortcut per elencare le attività
AppShortcut(
intent: ListTasksIntent(),
phrases: [
"Mostra le mie attività in \(.applicationName)",
"Visualizza le attività di \(.applicationName)",
"Quali sono le mie attività \(.applicationName)"
],
shortTitle: "Le mie attività",
systemImageName: "list.bullet"
)
// Shortcut con parametro dinamico
AppShortcut(
intent: CompleteTaskIntent(),
phrases: [
"Completa \(\.$taskName) in \(.applicationName)",
"Segna \(\.$taskName) come fatta con \(.applicationName)"
],
shortTitle: "Completa attività",
systemImageName: "checkmark.circle"
)
}
}Le frasi vocali devono includere il placeholder \(.applicationName) affinché Siri identifichi l'app di destinazione. I parametri dinamici come \(\.$taskName) consentono comandi contestuali.
Un'applicazione può dichiarare al massimo 10 App Shortcuts. Conviene dare priorità alle azioni più frequenti e utili per gli utenti.
Intents con parametri complessi
Gli intents possono accettare parametri complessi, incluse entità e opzioni di configurazione.
import AppIntents
struct CompleteTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Completa un'attività"
static var description = IntentDescription(
"Segna un'attività come completata."
)
// Parametro entità con ricerca automatica
@Parameter(title: "Attività")
var task: TaskEntity
// Parametro opzionale con data
@Parameter(title: "Data di completamento")
var completionDate: Date?
// Configurazione del dialogo Siri
static var parameterSummary: some ParameterSummary {
Summary("Completa \(\.$task)") {
\.$completionDate
}
}
func perform() async throws -> some IntentResult & ProvidesDialog {
// Aggiorna l'attività
TaskService.shared.completeTask(
id: task.id,
completionDate: completionDate ?? Date()
)
// Feedback vocale per Siri
return .result(
dialog: "L'attività \(task.title) è stata segnata come completata."
)
}
}
// Intent che restituisce una lista di entità
struct ListTasksIntent: AppIntent {
static var title: LocalizedStringResource = "Elenca attività"
// Filtro opzionale
@Parameter(title: "Stato", default: nil)
var statusFilter: TaskStatus?
@Parameter(title: "Priorità", default: nil)
var priorityFilter: TaskPriority?
func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> {
var tasks = TaskService.shared.fetchTasks()
// Applica i filtri
if let status = statusFilter {
tasks = tasks.filter {
switch status {
case .completed: return $0.isCompleted
case .pending, .inProgress: return !$0.isCompleted
}
}
}
if let priority = priorityFilter {
tasks = tasks.filter { $0.priority == priority }
}
let entities = tasks.map { TaskEntity(task: $0) }
return .result(value: entities)
}
}Il ParameterSummary definisce come Siri presenta l'intent durante l'esecuzione vocale, fornendo un feedback naturale.
Pronto a superare i tuoi colloqui su iOS?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Integrazione con Apple Intelligence
iOS 18 introduce gli App Intent Domains, raccolte di API progettate per funzionalità specifiche. Questi domini consentono ad Apple Intelligence di comprendere ed eseguire azioni con maggiore precisione.
import AppIntents
// Conformità al dominio Bookmarks per integrazione Apple Intelligence
struct SaveBookmarkIntent: AppIntent {
static var title: LocalizedStringResource = "Salva un segnalibro"
// Parametro URL validato automaticamente
@Parameter(title: "URL")
var url: URL
@Parameter(title: "Titolo", default: nil)
var title: String?
@Parameter(title: "Cartella", default: nil)
var folder: BookmarkFolderEntity?
// Apri l'app se necessario
static var openAppWhenRun: Bool = false
func perform() async throws -> some IntentResult & ProvidesDialog {
let bookmark = BookmarkService.shared.save(
url: url,
title: title,
folder: folder?.id
)
return .result(
dialog: "Segnalibro salvato: \(bookmark.title)"
)
}
}
// Intent con consapevolezza del contenuto a schermo (iOS 18.4+)
struct AnalyzeScreenContentIntent: AppIntent {
static var title: LocalizedStringResource = "Analizza contenuto"
// Accesso al contesto della schermata via Apple Intelligence
@Parameter(title: "Contesto")
var screenContext: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let context = screenContext else {
return .result(dialog: "Nessun contenuto da analizzare.")
}
// Elabora il contenuto estratto da Apple Intelligence
let analysis = ContentAnalyzer.analyze(context)
return .result(dialog: analysis.summary)
}
}I domini predefiniti (Books, Camera, Spreadsheets) permettono a Siri di rispondere alle richieste con precisione grazie a modelli addestrati su questi compiti specifici.
Azioni Siri con conferma utente
Per azioni sensibili, il sistema può richiedere conferma prima dell'esecuzione.
import AppIntents
struct DeleteTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Elimina un'attività"
@Parameter(title: "Attività")
var task: TaskEntity
// Richiede conferma utente
static var isDiscoverable: Bool = true
func perform() async throws -> some IntentResult & ProvidesDialog {
// Richiedi conferma tramite dialogo
try await requestConfirmation(
result: .result(
dialog: "Confermare l'eliminazione di \(task.title)?"
)
)
// Elimina dopo conferma
TaskService.shared.deleteTask(id: task.id)
return .result(
dialog: "L'attività \(task.title) è stata eliminata."
)
}
}
// Intent con più step di dialogo
struct ScheduleTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Pianifica un'attività"
@Parameter(title: "Attività")
var task: TaskEntity
@Parameter(title: "Data")
var scheduledDate: Date
@Parameter(title: "Promemoria", default: true)
var setReminder: Bool
static var parameterSummary: some ParameterSummary {
When(\.$setReminder, .equalTo, true) {
Summary("Pianifica \(\.$task) per \(\.$scheduledDate) con promemoria")
} otherwise: {
Summary("Pianifica \(\.$task) per \(\.$scheduledDate)")
}
}
func perform() async throws -> some IntentResult & ProvidesDialog {
TaskService.shared.schedule(
taskId: task.id,
date: scheduledDate,
reminder: setReminder
)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
let formattedDate = dateFormatter.string(from: scheduledDate)
return .result(
dialog: "Attività pianificata per \(formattedDate)."
)
}
}Il metodo requestConfirmation mette in pausa l'esecuzione fino alla conferma dell'utente, proteggendo da azioni accidentali.
Intents nei widget interattivi
Gli App Intents si integrano naturalmente con WidgetKit per creare widget interattivi su iOS 17+.
import AppIntents
import WidgetKit
// Intent ottimizzato per widget (esecuzione rapida)
struct ToggleTaskFromWidgetIntent: AppIntent {
static var title: LocalizedStringResource = "Alterna attività"
@Parameter(title: "ID attività")
var taskID: String
init() {}
init(taskID: UUID) {
self.taskID = taskID.uuidString
}
// Nessun dialogo per i widget
func perform() async throws -> some IntentResult {
guard let uuid = UUID(uuidString: taskID) else {
return .result()
}
TaskService.shared.toggleCompletion(taskId: uuid)
// Aggiornamento immediato del widget
WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")
return .result()
}
}
// View del widget con bottone interattivo
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.
Configurazione dell'Action Button
L'Action Button su iPhone 15 Pro e successivi può attivare App Shortcuts direttamente.
import AppIntents
// Intent ottimizzato per Action Button
struct QuickCaptureIntent: AppIntent {
static var title: LocalizedStringResource = "Cattura rapida"
static var description = IntentDescription(
"Crea rapidamente un'attività con un titolo."
)
// Apre l'app per l'input
static var openAppWhenRun: Bool = true
func perform() async throws -> some IntentResult & OpensIntent {
// Notifica per aprire la schermata di cattura rapida
NotificationCenter.default.post(
name: .quickCaptureTriggered,
object: nil
)
return .result(opensIntent: ShowQuickCaptureViewIntent())
}
}
// Dichiarare in AppShortcutsProvider per Action Button
extension TaskAppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
// ... altri shortcut
AppShortcut(
intent: QuickCaptureIntent(),
phrases: [
"Cattura rapida \(.applicationName)",
"Nota rapida \(.applicationName)"
],
shortTitle: "Cattura",
systemImageName: "bolt.circle"
)
}
}Gli utenti possono configurare l'Action Button per attivare questo shortcut tramite Impostazioni > Tasto Azione.
iOS 18 introduce i macro @DeferredProperty e @ComputedProperty per ridurre il boilerplate. Gli App Intents possono inoltre risiedere in Swift Packages per il riutilizzo cross-platform.
Intents in Swift Packages
Gli App Intents possono essere definiti in Swift Packages per la condivisione tra iOS, macOS e watchOS.
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "TaskIntents",
platforms: [
.iOS(.v17),
.macOS(.v14),
.watchOS(.v10)
],
products: [
.library(name: "TaskIntents", targets: ["TaskIntents"])
],
dependencies: [],
targets: [
.target(
name: "TaskIntents",
dependencies: []
)
]
)
// Sources/TaskIntents/SharedIntents.swift
import AppIntents
// Intent condiviso multipiattaforma
public struct SharedCreateTaskIntent: AppIntent {
public static var title: LocalizedStringResource = "Crea un'attività"
@Parameter(title: "Titolo")
public var taskTitle: String
public init() {}
public func perform() async throws -> some IntentResult {
// Logica condivisa
await TaskRepository.shared.create(title: taskTitle)
return .result()
}
}
// Estensione specifica per piattaforma nell'app
#if os(iOS)
extension SharedCreateTaskIntent {
// Comportamento specifico iOS
static var openAppWhenRun: Bool = false
}
#endifQuesta architettura mantiene un'unica base di codice per gli intents adattando il comportamento per piattaforma.
Testare gli App Intents
Gli App Intents possono essere testati a livello unitario come qualsiasi codice Swift.
import XCTest
import AppIntents
@testable import TaskApp
final class TaskIntentTests: XCTestCase {
override func setUp() {
super.setUp()
// Reset del service per test isolati
TaskService.shared.reset()
}
func testCreateTaskIntent() async throws {
// Given
var intent = CreateTaskIntent()
intent.taskTitle = "Attività di test"
intent.priority = .high
// When
let result = try await intent.perform()
// Then
let tasks = TaskService.shared.fetchTasks()
XCTAssertEqual(tasks.count, 1)
XCTAssertEqual(tasks.first?.title, "Attività di test")
XCTAssertEqual(tasks.first?.priority, .high)
}
func testCompleteTaskIntent() async throws {
// Given
let task = TaskService.shared.createTask(
title: "Attività da completare",
priority: .medium
)
var intent = CompleteTaskIntent()
intent.task = TaskEntity(task: task)
// When
_ = try await intent.perform()
// Then
let updatedTask = TaskService.shared.fetchTask(id: task.id)
XCTAssertTrue(updatedTask?.isCompleted ?? false)
}
func testEntityQuery() async throws {
// Given
let task1 = TaskService.shared.createTask(title: "Attività 1", priority: .low)
let task2 = TaskService.shared.createTask(title: "Attività 2", priority: .high)
let query = TaskEntityQuery()
// When
let entities = try await query.entities(for: [task1.id, task2.id])
// Then
XCTAssertEqual(entities.count, 2)
}
}I test verificano il comportamento degli intents indipendentemente dall'interfaccia di sistema.
Best practice e ottimizzazioni
Diversi pattern garantiscono App Intents performanti e un'esperienza utente ottimale.
import AppIntents
// 1. Frasi localizzate
struct LocalizedTaskIntent: AppIntent {
static var title: LocalizedStringResource = "task.create.title"
static var description = IntentDescription(
"task.create.description",
categoryName: "task.category"
)
@Parameter(title: "task.parameter.title")
var taskTitle: String
func perform() async throws -> some IntentResult {
// ...
return .result()
}
}
// 2. Gestione corretta degli errori
enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible {
case taskNotFound
case invalidInput
case serviceUnavailable
var localizedStringResource: LocalizedStringResource {
switch self {
case .taskNotFound:
return "Attività non trovata."
case .invalidInput:
return "Dati non validi."
case .serviceUnavailable:
return "Servizio temporaneamente non disponibile."
}
}
}
struct RobustTaskIntent: AppIntent {
static var title: LocalizedStringResource = "Intent robusto"
@Parameter(title: "ID")
var taskId: String
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let uuid = UUID(uuidString: taskId) else {
throw TaskIntentError.invalidInput
}
guard let task = TaskService.shared.fetchTask(id: uuid) else {
throw TaskIntentError.taskNotFound
}
return .result(dialog: "Attività trovata: \(task.title)")
}
}
// 3. Ottimizzazione di EntityQuery
struct OptimizedTaskQuery: EntityStringQuery {
// Ricerca testuale ottimizzata
func entities(matching string: String) async throws -> [TaskEntity] {
// Ricerca sul service con limite
TaskService.shared.search(query: string, limit: 10)
.map { TaskEntity(task: $0) }
}
// Suggerimenti limitati per le performance
func suggestedEntities() async throws -> [TaskEntity] {
TaskService.shared.fetchRecentTasks(limit: 5)
.map { TaskEntity(task: $0) }
}
}
// 4. Focus Filter per le modalità Full Immersion
struct TaskFocusFilter: SetFocusFilterIntent {
static var title: LocalizedStringResource = "Filtra attività"
@Parameter(title: "Mostra solo priorità alta")
var showHighPriorityOnly: Bool
func perform() async throws -> some IntentResult {
TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly)
return .result()
}
}Questi pattern garantiscono un'integrazione fluida con il sistema preservando performance ottimali.
Conclusione
App Intents e Siri Shortcuts trasformano il modo in cui gli utenti interagiscono con le applicazioni iOS. Nel 2026, con Apple Intelligence, esporre intents non è più opzionale ma essenziale per offrire un'esperienza moderna e intuitiva.
Checklist App Intents iOS 18+
- ✅ 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 - ✅ Configurare ParameterSummary per il feedback Siri
- ✅ Integrare i domini Apple Intelligence se applicabile
- ✅ Testare gli intents con test unitari
- ✅ Localizzare titoli e descrizioni
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Tag
Condividi
Articoli correlati

WidgetKit iOS 17+: Widget Interattivi con App Intents
Guida completa per creare widget iOS interattivi con WidgetKit e App Intents. Pulsanti, toggle, animazioni e best practice per iOS 17+ nel 2026.

Combine vs async/await in Swift: Pattern di Migrazione Progressiva
Guida completa alla migrazione da Combine ad async/await in Swift: strategie progressive, pattern di bridging e coesistenza dei paradigmi nelle codebase iOS.

Domande di colloquio sull'accessibilità iOS nel 2026: VoiceOver e Dynamic Type
Preparati ai colloqui iOS con domande chiave sull'accessibilità: VoiceOver, Dynamic Type, trait semantici e audit.