App Intents 2.0 y Siri Shortcuts: guía de automatización iOS 27

Guía completa de App Intents 2.0 y Siri Shortcuts para iOS 27. Crear streaming responses, conversaciones multi-turno, View Annotations e integrar con Foundation Models.

App Intents y Siri Shortcuts para automatización iOS avanzada con Swift y Apple Intelligence

App Intents 2.0 y Siri Shortcuts representan la única vía para que las apps de terceros se integren con Siri en iOS 27. Tras WWDC 2026, Apple deprecó SiriKit y posicionó App Intents como el framework obligatorio para la interacción por voz, descubrimiento en Spotlight y flujos de automatización.

Lo que cubre este artículo

Este artículo presenta la creación completa de App Intents y Siri Shortcuts para iOS 27, desde los conceptos fundamentales hasta streaming responses, conversaciones multi-turno y la API View Annotations.

Comprender el framework App Intents 2.0

El framework App Intents, introducido con iOS 16 y expandido a la versión 2.0 en WWDC 2026, es un framework Swift-nativo y declarativo para construir acciones detectables por el sistema. iOS 27 incorpora cuatro novedades principales: streaming responses para operaciones de larga duración, seguimientos conversacionales multi-turno, View Annotations para referenciar elementos en pantalla, y App Schemas para comprensión semántica sin frases de entrenamiento.

En WWDC 2026, Apple deprecó formalmente SiriKit y estableció App Intents como la única forma en que Siri puede interactuar con apps de terceros. El código SiriKit existente continúa compilando con advertencias de deprecación, pero Apple ha señalado una ventana de soporte de dos a tres años antes de su eliminación.

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

El intent declara sus parámetros mediante el property wrapper @Parameter, permitiendo a Siri solicitar los valores faltantes conversacionalmente. El método perform() ejecuta la lógica de negocio y retorna un resultado tipado. El sistema de tipos sirve como el schema que Siri AI descubre dinámicamente.

Streaming Responses para acciones de larga duración

App Intents 2.0 introduce streaming responses, permitiendo a los intents reportar progreso durante la ejecución en lugar de bloquearse hasta completar. Esta capacidad aborda escenarios como subida de archivos, sincronización de datos o cálculos complejos donde el usuario necesita 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)."
        )
    }
}

Las streaming responses permiten feedback natural durante operaciones que toman más de unos segundos, manteniendo el engagement del usuario en lugar de dejarlo esperando en silencio.

Seguimientos conversacionales multi-turno

Las conversaciones multi-turno permiten a Siri hacer preguntas clarificadoras y continuar la interacción dentro de una sola sesión. Esta característica transforma los App Intents de comandos de un solo disparo a flujos de trabajo conversacionales.

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

El método requestValue(for:dialog:) pausa la ejecución hasta que Siri recibe la respuesta del usuario, creando un intercambio natural de ida y vuelta en lugar de requerir todos los parámetros por adelantado.

API View Annotations para referencias en pantalla

La API View Annotations, nueva en iOS 27, permite a los usuarios referenciar elementos de UI directamente en comandos de Siri usando frases como "esta foto", "el tercero" o "ese mensaje". Esta capacidad requiere anotar vistas SwiftUI con información semántica.

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

Cuando un usuario dice "comparte esta foto" mientras ve la cuadrícula, Siri resuelve "esta" a la PhotoEntity actualmente enfocada o tocada por última vez a través de la anotación. Esto elimina la fricción de describir elementos verbalmente.

Compatibilidad de dispositivos

View Annotations requiere iOS 27, pero la capa App Intents funciona en todos los dispositivos iOS 27 incluyendo iPhone 11 y posteriores. Las características Apple Intelligence como la conciencia en pantalla necesitan iPhone 15 Pro o posterior.

Definir App Entities para los datos

Las App Entities representan los "sustantivos" de la aplicación: los objetos sobre los que operan los intents. Permiten a Siri comprender, buscar y manipular los datos de la app. Construir entidades efectivas es esencial para los patrones de gestión de estado en SwiftUI en apps orientadas a 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) }
    }
}

El protocolo EntityStringQuery añade capacidad de búsqueda textual, permitiendo a Siri encontrar entidades por nombre. El método suggestedEntities() alimenta las interfaces de Siri y Shortcuts con opciones relevantes.

Usar AppEnum para valores fijos

Usar AppEnum para tipos con un conjunto fijo de valores (prioridad, estado), y AppEntity para tipos dinámicos creados por el usuario (tareas, notas, contactos). Mezclarlos causa complejidad innecesaria.

Crear App Enums para valores fijos

Los App Enums exponen tipos enumerados al sistema, permitiendo a Siri ofrecer opciones contextuales con representaciones visuales.

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

Los iconos SF Symbols enriquecen la visualización en Shortcuts y las sugerencias de Siri, haciendo la selección más rápida que las descripciones verbales.

¿Listo para aprobar tus entrevistas de iOS?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Implementar AppShortcutsProvider

El AppShortcutsProvider expone los App Shortcuts al sistema, dejándolos disponibles inmediatamente sin configuración del usuario. Estos atajos aparecen en Spotlight, Siri, el Action Button y los nuevos flujos agénticos 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"
        )
    }
}

Las frases de voz deben incluir el placeholder (.applicationName) para que Siri identifique la app destino. Los parámetros dinámicos como (.$taskName) permiten comandos contextuales.

Intents en widgets interactivos

Los App Intents se integran con WidgetKit para crear widgets interactivos. Comprender los patrones de navegación en SwiftUI ayuda al construir transiciones de widget a 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)
    }
}

Los widgets utilizan la sintaxis Button(intent:) para conectar la interacción directamente con el App Intent sin abrir la aplicación.

Integración con Foundation Models Framework

iOS 27 introduce el Foundation Models framework, habilitando inferencia LLM en dispositivo. Aunque Foundation Models no tiene integración directa con Siri, las apps pueden usarlo dentro de App Intents para procesar lenguaje natural o generar respuestas.

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 ejecuta aproximadamente 30 tokens por segundo en dispositivo sin costo por solicitud. El framework requiere iPhone 15 Pro o iPhone 16 y posterior para procesamiento en dispositivo.

Migrar de SiriKit a App Intents

Para apps que aún usan SiriKit, la migración sigue un proceso estructurado. Xcode 27 proporciona una herramienta "Convert to App Intent" para configuraciones de Widget, pero los handlers personalizados requieren reescritura manual.

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 migración elimina los archivos .intentdefinition y los targets Intent Extension. Toda la lógica de intents se mueve al target principal de la app como structs Swift.

Probar los App Intents

El framework AppIntentsTesting valida intents a través de la infraestructura real de Siri, Shortcuts y Spotlight sin 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)
    }
}

Los tests verifican el comportamiento de los intents independientemente de la interfaz del sistema, detectando regresiones antes del despliegue.

Sources

Checklist de implementación App Intents iOS 27

Para apps que apuntan a iOS 27, los App Intents ya no son opcionales. Siri AI compone acciones de múltiples pasos a través de apps, y las apps sin intents publicados quedan excluidas de estos flujos agénticos.

  • Crear AppIntents para las acciones principales de la app
  • Definir AppEntities para los datos manipulables
  • Usar AppEnum para los tipos enumerados
  • Implementar AppShortcutsProvider con frases de voz
  • Respetar el límite máximo de 10 App Shortcuts
  • Incluir (.applicationName) en todas las frases
  • Añadir View Annotations a las vistas SwiftUI para referencias en pantalla
  • Implementar streaming responses para operaciones de larga duración
  • Soportar conversaciones multi-turno para flujos de trabajo complejos
  • Migrar de SiriKit antes de que cierre la ventana de deprecación
  • Probar intents con el framework AppIntentsTesting
  • Localizar títulos y descripciones

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Reto diario

¿Sabrías detectar el bug en iOS?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 20 de agosto de 2026

Etiquetas

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

Compartir

Artículos relacionados