View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Temas
  • Todos los videos
  • Información

Volver a WWDC25

  • Información
  • Resumen
  • Transcripción
  • Código
  • Explora nuevos avances en App Intents

    Explora todas las nuevas mejoras disponibles en la estructura App Intents en las versiones de este año. Obtén información sobre las mejoras en la calidad de vida de los desarrolladores, como propiedades diferidas, nuevas capacidades como fragmentos de App Intents interactivos, anotaciones de vista de entidad, cómo integrar Visual Intelligence y mucho más. Te mostraremos cómo App Intents es más expresivo que nunca y, al mismo tiempo, se vuelve más fácil y sencillo de adoptar. También compartiremos nuevos y emocionantes clientes de App Intents este año, como Spotlight y Visual Intelligence, y aprenderemos a escribir App Intents que funcionen muy bien en esos contextos.

    Capítulos

    • 0:00 - Introducción
    • 0:55 - Fragmentos interactivos
    • 8:15 - Nuevas integraciones de sistemas
    • 15:01 - Mejoras en la experiencia de usuario
    • 21:02 - API de conveniencia

    Recursos

    • Accelerating app interactions with App Intents
    • Adopting App Intents to support system experiences
    • App intent domains
    • App Intents
    • App Shortcuts
    • Building a workout app for iPhone and iPad
    • Creating your first app intent
    • Integrating actions with Siri and Apple Intelligence
    • Making actions and content discoverable and widely available
    • PurchaseIntent
      • Video HD
      • Video SD

    Videos relacionados

    WWDC25

    • Conoce App Intents
    • Desarrolla para Atajos y Spotlight con App Intents
    • Diseña fragmentos interactivos

    WWDC24

    • Bring your app to Siri
    • Bring your app’s core features to users with App Intents
    • Design App Intents for system experiences
    • What’s new in App Intents
  • Buscar este video…

    Hola, mi nombre es Jeff, soy ingeniero del equipo de App Intents. Hoy exploraremos nuevos avances en App Intents.

    App Intents le brinda a tu app la capacidad de integrar tus funcionalidades en todos los dispositivos de cada persona, incluso en lugares como Atajos, Spotlight y Inteligencia Visual.

    Si no conoces la estructura, te sugiero que consultes primero la sesión "Conoce App Intents". Estaré encantado de tenerte de vuelta en cualquier momento. Tenemos mucho que repasar hoy, incluidas nuevas experiencias que puedes crear con snippets interactivos, formas adicionales en las que tu app puede aprovechar App Intents en todo el sistema, más funcionalidades que puedes usar para refinar las experiencias de usuario y varias API de útiles que agregamos para mejorar tu experiencia al desarrollar apps. Comencemos con los fragmentos interactivos.

    Los fragmentos permiten que tu app muestre vistas personalizadas con App Intents, ya sea para solicitar confirmación o mostrar un resultado. Ahora puedes dar vida a esos fragmentos con interactividad. Por ejemplo, puedes crear un fragmento de jardinería que sugiera encender los aspersores cuando el suelo esté demasiado seco. O configurar un pedido de comida antes de enviarlo al restaurante. ¿Qué tal si lo integramos con otras funcionalidades, como Actividades en Vivo, para comenzar a seguir inmediatamente el juego después de verificar los puntajes? Aquí te hago una demostración rápida de un fragmento interactivo con nuestra app de muestra. Luego exploraremos su implementación.

    La app TravelTracking contiene muchos puntos de referencia en todo el mundo. Tocaré el control que ejecuta una App Intent para encontrar el punto de referencia más cercano.

    Después de ubicarlo, la intent muestra un fragmento con el punto de referencia con un botón de corazón junto al título. Soy de Toronto. Las Cataratas del Niágara están prácticamente en mi patio trasero, así que tocaré el botón del corazón para agregarlas a mis favoritos. El fragmento se actualizará inmediatamente para mostrar su nuevo estado. Esto se construye adoptando el nuevo protocolo Snippet Intent. Estos Intents representan las vistas según sus parámetros y el estado de la app. Puedes usarlos para mostrar resultados después de una acción o solicitar confirmación. Primero, exploremos cómo funcionan los fragmentos de resultados. Cualquier intent en tu app puede devolver un fragmento de intent lleno de valores de parámetros como parte de su resultado. El sistema usa esto cada vez que necesita actualizar el fragmento. Cuando eso sucede, el sistema completa los parámetros de Snippet Intent con los valores que especificaste. Si alguna de ellas es una entidad de app, se obtendrán de sus consultas.

    A continuación, el sistema ejecuta el método de ejecución de Snippet Intent. Allí, la intent puede acceder a sus parámetros y al estado de la app para representar la vista y luego devolverla como parte del resultado. La vista puede asociar botones o alternadores con cualquier App Intent de tu app.

    En el ejemplo, el botón de corazón ejecuta la intent Actualizar favoritos y el botón de buscar tickets ejecuta la intent Buscar tickets. No es necesario crear estas intent solo para fragmentos. De hecho, puedes reusar las existentes sin modificarlas.

    Cuando se presiona el botón de corazón, el sistema ejecuta la intent correspondiente y espera a que se complete. Esto garantiza que el estado de favorito esté actualizado. Una vez que eso finalice, el sistema usa Snippet Intent parametrizado que devolvió antes para activar otra actualización. Al igual que antes, completa los parámetros con los valores que hayas especificado. Cualquier entidad de la app también se recupera nuevamente de sus consultas. Luego, el sistema ejecuta el método de ejecución, que le da a Snippet Intent la oportunidad de generar una vista actualizada. En este caso, el ícono del corazón ahora está relleno. Cualquier cambio en la vista se anima de acuerdo con las API contentTransition de SwiftUI. El ciclo continúa hasta que se descarta el fragmento.

    Implementemos la devolución de una Snippet Intent. Primero, tomo la intent existente y agrego ShowsSnippetIntent a su tipo de retorno. Luego, proporciono snippetIntent usando el nuevo parámetro en el método de resultado. Esto le indica al sistema que complete el parámetro con el punto de referencia dado cada vez que se usa snippetIntent.

    Ahora implementemos snippetIntent en sí. En primer lugar, debe cumplir con el protocolo SnippetIntent. Luego agregaré las variables que necesita para representar la vista correctamente. Deben estar marcados como parámetros, de lo contrario no serán rellenados por el sistema. Y como es un App Intent, también tiene acceso a AppDependencies. En el tipo de retorno de los métodos de ejecución, agrega ShowsSnippetView. Y en el cuerpo, usa métodos para obtener los estados necesarios para producir la vista y luego devuélvalos usando el parámetro view en un método de resultado.

    De manera similar a las vistas de SwiftUI, SnippetIntent se creará y se ejecutará muchas veces durante su ciclo de vida. Por lo tanto, no alteres el estado de la app. El sistema incluso podría ejecutarlo más veces para reaccionar a los cambios de dispositivo, como pasar al Modo oscuro. Tampoco muestres las vistas rápidamente, para que no parezca que no responden.

    Y dado que solo especifica los valores del parámetro una vez, solo debes usarlos para los AppEntity, que se consultan cada vez, y valores primitivos que nunca cambiarán. Todos los demás valores deben obtenerse en el método de ejecución.

    Con el fragmento creado, ahora exploraremos cómo la vista SwiftUI activa las intents.

    En el cuerpo de la Vista, LandmarkView usa el inicializador del botón para asociar las App Intents correspondientes. También existen API similares para alternadores.

    Estas API se introdujeron con widgets interactivos junto con el modificador contentTransition para personalizar animaciones.

    Si quieres saber más, consulta la charla de Luca de 2023. Ahora, implementemos la funcionalidad de búsqueda de tickets usando un snippet de confirmación.

    Si toco el botón Buscar entradas, me preguntará cuántas entradas quiero buscar. Lo aumentaré a cuatro porque iré con amigos y luego empezaré la búsqueda. Finalmente, mostrará el precio más barato que pueda encontrar utilizando un snippet de resultado. Vamos a verlo paso a paso. A partir del snippet de resultado, si tus botones o alternadores activan intents que presentan sus propios snippets, reemplazarán el original. Tenga en cuenta que esto solo funciona si el original mostraba un resultado. La intent de seguimiento puede presentar cualquier tipo de snippet, pero aquí usamos la confirmación de solicitud para presentar la vista de configuración. Para ello, simplemente invoca el método requestConfirmation. Puedes personalizar el nombre de la acción y proporcionar la snippetIntent que impulsa la experiencia de confirmación.

    Este método genera un error si el snippet se cancela en cualquier momento. No intentes cortarlo, simplemente deja que termine tu método de rendimiento.

    Luego, cada interacción con la vista pasará por el mismo ciclo de actualización que el snippet de resultado.

    En el ejemplo, la Snippet Intent es siempre mostrar el número actualizado de tickets. ¿Cómo lo hace? Bueno, debido a que modelé searchRequest como una AppEntity, el sistema siempre obtendrá el valor más nuevo de la consulta al completar este parámetro. De esta manera puedo pasarlo a mi vista y obtener automáticamente los últimos valores.

    El sistema no finaliza la app mientras el snippet esté visible, así que siéntete libre de conservar los estados en la memoria. No es necesario almacenarlos en una base de datos.

    Después de todas las interacciones, cuando finalmente se toca la acción de búsqueda, se reanuda la ejecución de App Intents original.

    A veces, es posible que desees que tu fragmento se actualice en medio de una tarea porque hay un nuevo estado.

    En estos escenarios, invoca el método de recarga estático en la intent del snippet que debe actualizarse.

    Consulta esta sesión para obtener más información sobre cómo diseñar los mejores snippets interactivos. Los fragmentos fueron solo el comienzo. Ahora exploraremos aún más formas en las que App Intents pueden ayudarte a integrar tu app en todo el sistema. La primera de ellas es la búsqueda de imágenes. Esta funcionalidad permite a las personas realizar búsquedas directamente desde una captura de cámar o una captura de pantalla. Como novedad en iOS 26, los resultados de búsqueda de tu app también podrán aparecer. Aquí tengo una captura de pantalla de un punto de referencia. Lo resaltaré para realizar una búsqueda de imágenes y el sistema mostrará un panel de búsqueda. Puedo seleccionar la app TravelTracking para ver sus resultados. Luego, cuando toco un resultado, se abrirá la app en la página del punto de referencia correspondiente.

    Para admitir la búsqueda de imágenes, implementa una consulta que se ajuste al protocolo IntentValueQuery. Aceptará el tipo SemanticContentDescriptor como entrada y devolverá una matriz de App Entities. La búsqueda de imágenes mostrará las App Entities usando las representaciones de visualización.

    Cuando se toca un resultado, la AppEntity correspondiente se enviará a su OpenIntent para que tu app lo maneje. Esta OpenIntent debe existir, de lo contrario, tu app no aparecerá.

    Para implementar la consulta, comienza con una estructura que se ajuste al protocolo IntentValueQuery. El método de valores debe tener SemanticContentDescriptor como entrada. Contiene los pixeles del área seleccionada. Puedes usar las API Video Toolbox o CoreImage para convertirlo en un tipo familiar, como CGImage.

    Después de realizar la búsqueda, devuelve una matriz de las entidades coincidentes. A continuación, implementa una intent que se ajuste al protocolo OpenIntent para permitir tocar el resultado. El parámetro target debe tener el mismo tipo de entidad que los resultados. OpenIntents no solo sirve para admitir la Búsqueda de imágenes. También se pueden usar en otros lugares como Spotlight, lo que brinda a los usuarios una forma sencilla de ir a una entidad en tu app.

    Las personas tendrán la mejor experiencia si pueden encontrar rápidamente lo que buscan. Por lo tanto, devuelve algunas páginas de resultados para aumentar la probabilidad de que aparezca la coincidencia perfecta en la UI del sistema. Puedes usar los servidores para consultar un conjunto de datos más grande. Sin embargo, no busques durante demasiado tiempo. De lo contrario, sentirás que no responde. Por último, permíteles continuar la experiencia de búsqueda en tu app si no encuentran lo que buscan. Agreguemos eso a TravelTracking.

    Cuando no veo el resultado que quiero en la lista, puedo tocar el botón Más resultados, que abre mi app y me lleva a la vista de búsqueda. Para implementar esto, usa la nueva macro AppIntent para especificar el esquema semanticContentSearch. Esta es la nueva API que reemplaza la macro AssistantIntent porque ampliamos los esquemas a funciones fuera del asistente, como visualIntelligence. Agrega la propiedad semanticContent requerida por el esquema. La macro lo marcará automáticamente como un parámetro de intent.

    En el método de ejecución, procesa los metadatos de búsqueda y luego ve a la vista de búsqueda. Como funcionalidad adicional, quiero que TravelTracking muestre colecciones que contengan los puntos de referencia coincidentes. Pero, ¿cómo puedo tener una consulta que devuelva una mezcla de LandmarkEntity y CollectionEntity? La respuesta es UnionValues. Primero, declara un UnionValue con cada caso que represente un tipo de entidad que la consulta podría devolver. Luego, cambia el tipo de resultado para que sea una matriz de ellos. Ahora puedo devolver una mezcla de ambos. Por supuesto, no olvides implementar una OpenIntent para cada tipo de entidad. Para obtener más información sobre UnionValues, consulta la sesión de App Intents de Kenny de 2024.

    Es fantástico que el contenido de tu app se pueda buscar fuera de la misma, pero hay más que puedes hacer con Apple Intelligence cuando tu app está activa. Hablemos de las entidades en pantalla. Al usar NSUserActivities, tu app tiene la capacidad de asociar entidades con el contenido en pantalla. Esto permite que las personas pregunten a ChatGPT sobre cosas actualmente visibles en tu app. En la app TravelTracking, mientras miro las Cataratas del Niágara, puedo preguntarle a Siri si ese lugar está cerca del océano. Siri comprenderá que me refiero a la vista en la pantalla y ofrecerá enviar una captura de pantalla a ChatGPT. Pero LandmarkEntity admite PDF, así que elegiré la opción de contenido completo. Puedo darle una vista previa rápida y luego enviarlo.

    Se mostrará la respuesta y aprendí que, si bien son grandes, los Grandes Lagos NO son un océano. Para asociar una entidad con una vista, comenzaremos agregando el modificador userActivity a LandmarkDetailView. Luego, en el cierre de actualización, asocie un identificador de entidad con la actividad. Después, necesitamos permitir la conversión de LandmarkEntity en un tipo de datos que ChatGPT pueda entender, como un PDF.

    Lo hacemos primero adaptándolo al protocolo Transferable y luego proporcionando la representación de datos en PDF. Otros tipos admitidos incluyen texto simple y texto enriquecido. Consulta esta documentación de App Intents para obtener más información sobre las entidades en pantalla.

    Hablando de mostrar el contenido de tu app al sistema, quiero hablar brevemente sobre Spotlight. Ahora que admitimos la ejecución de acciones directamente desde Spotlight en la Mac, hay algunas cosas que puedes hacer con App Intents para brindar la mejor experiencia.

    Primero, haz que las entidades de la app se ajusten a IndexedEntity y dónalas a Spotlight. Esto permite que la búsqueda de Spotlight impulse la experiencia de filtrado de tus parámetros. Para asociar propiedades de entidad con las claves de Spotlight, ahora puedes usar el nuevo parámetro indexingKey en el atributo Property. En este ejemplo, le di al continente una clave de indexación personalizada. Esto permitirá a las personas buscar lugares de referencia asiáticos escribiendo “Asia” en el campo de texto. Como beneficio adicional por adoptar la API, esto permite que la app Atajos genere automáticamente acciones de búsqueda para las entidades.

    En segundo lugar, anota el contenido que aparece en pantalla con entidades, de modo que estas entidades tengan prioridad en las sugerencias cuando las vistas sean visibles. En tercer lugar, implementa PredictableIntent para que el sistema pueda aprender y brindar sugerencias para tus Intents en función de las Intents y los parámetros del comportamiento anterior del usuario. Incluso puedes proporcionar descripciones personalizadas en función de sus valores.

    Si desea obtener más información sobre esta funcionalidad, consulta esta sesión sobre Atajos y Spotlight. Ahora que tienes estas funcionalidades en tu app, te mostraré algunas formas nuevas de refinar las experiencias de los usuarios de tu app. Empecemos con Deshacer. Las personas tienen más probabilidades de probar cosas diferentes si saben que pueden cambiar de opinión. Por eso es importante que puedan revertir las acciones realizadas con tu app. Con el nuevo protocolo UndoableIntent, puede permitir fácilmente que las personas deshagan las intenciones de tu app con gestos que ya conocen.

    Aquí se muestran algunas de mis colecciones en la app TravelTracking. Usaré Escríbele a Siri para ejecutar uno de mis atajos personalizados para eliminar la colección Sweet Deserts. Después de confirmar la eliminación, se quita de la app. Si cambio de opinión ahora, puedo deslizar tres dedos hacia la izquierda para deshacer y restaurar la colección.

    DeleteCollectionIntent participa en la pila de deshacer adaptándose primero al protocolo UndoableIntent, que proporciona una propiedad undoManager opcional con la que puedes registrar las acciones de deshacer. También están disponibles otras operaciones como setActionName. El sistema le da a tu intent el administrador de deshacer más relevante a través de esta propiedad, incluso cuando esas intents se ejecutan en tus extensiones. Esto garantiza que las acciones de deshacer de tu app en la UI y App Intents permanezcan sincronizadas, para que se puedan deshacer en el orden correcto. Refinemos aún más esta intent ofreciendo alternativas a la eliminación.

    Podemos usar la nueva API Multiple Choice para presentar varias opciones para que las personas elijan. Ejecutaré la intent que elimina la colección nuevamente. Esta vez, en lugar de simplemente pedirme que confirme la eliminación, proporciona una opción adicional para archivarlo.

    Esta colección me trae recuerdos. Creo que la archivaré. En el método de ejecución, presenta el snippet de opción múltiple usando el método requestChoice y proporcione una matriz de opciones usando el parámetro between. Se pueden crear opciones con títulos personalizados. También puedes especificar un estilo que le indique al sistema cómo representarlo.

    Luego, puedes personalizar el snippet con un cuadro de diálogo y una vista SwiftUI personalizada. Cuando se selecciona una opción, se devuelve desde el método requestChoice, excepto en el caso de cancelación, que genera un error que finaliza el método de ejecución. No deberías encontrarte con este error. Si se cancela una solicitud, la intent debe detenerse inmediatamente.

    Luego, como seguimiento, usa una declaración switch para ramificar en la opción elegida. Puedes usar las opciones originales que creaste como valores esperados. Hablemos de un área más del pulido. Los modos compatibles brindan a tus intents un mayor control sobre la puesta en primer plano de la app. Esto les permite comportarse de manera diferente dependiendo de cómo el usuario interactúa con el dispositivo. Por ejemplo, si estuviera conduciendo, quiero que las intents me den información únicamente mediante voz. Pero si estuviera mirando el dispositivo, debería llevarme directamente a la app porque allí puede mostrarme más información. Con esta funcionalidad, puedo implementar un App Intent que haga ambas cosas. Te muestro cómo.

    Aquí está la intent de obtener el estado de multitud para las Cataratas del Niágara.

    Desactivaré la opción Abrir al ejecutar. Esto se parece a los escenarios en los que no es posible poner en primer plano la imagen, como usar Siri mientras se usan auriculares. Cuando lo ejecuto, solo se mostrará un cuadro de diálogo, que también podrá leer Siri.

    Pero si lo ejecuto con el cuadro de diálogo desactivado y activo Abrir al ejecutar, me llevará directamente a la página del punto de referencia que muestra la ocupación y el clima actual.

    Agreguemos esta funcionalidad a la intent.

    Primero, agregaré los modos admitidos con la variable estática. Si lo configuro solo en segundo plano, le dice al sistema que la app nunca será puesta en primer plano por la intent. Sin embargo, también quiero que la intent me lleve a la app si es posible. Entonces agregaré el modo de primer plano, que le pide al sistema que inicie la app antes de ejecutar la intent.

    Luego puedo usar la nueva propiedad currentMode para verificar si estamos en primer plano. Si es así, podremos navegar en consecuencia. Espera un momento. ¿Por qué dice que no hay nadie en el punto de referencia? Está cerrado. Modifiquemos la intent para que no abra la app en este escenario. En el método de ejecución, puedo verificar si el punto de referencia está abierto y salir antes si está cerrado. En este caso, no quiero que el sistema ponga la app en primer plano antes de ejecutar el intent. Así que modificaré el modo de primer plano para que sea dinámico. Los modos admitidos disponibles incluyen segundo plano y tres modos de primer plano. Está Inmediato, que le dice al sistema que ponga la app en primer plano antes de ejecutar la intent. Dinámico, que permite que la intent decida si iniciar o no la app. Y Diferido, lo que indica que la intent eventualmente pondrá en primer plano la app, pero no de inmediato. Es posible que GetCrowdStatus no inicie la app, por lo que la opción Dinámica es la opción perfecta.

    Para estos dos modos, la intent puede usar el método continueInForeground para controlar exactamente cuándo llevar la app al primer plano.

    Una vez que estemos seguros de que el punto de referencia está abierto, podemos usar la nueva propiedad en systemContext para verificar si podemos poner en primer plano la app. Si podemos, usaremos el método continueInForeground para ponerla en primer plano. Al configurar alwaysConfirm como false, el sistema evita preguntar si hubo actividad en los últimos segundos. Si la ejecución de la app fue exitosa, finalmente podremos navegar a la vista del estado de la multitud. Sin embargo, si la solicitud de ejecución es denegada, ya sea por el sistema o por el usuario, el método arroja un error. Puedes manejarlo adecuadamente. Antes de terminar, quiero repasar algunas formas en las que hicimos que el desarrollo con App Intents sea más fácil para ti.

    Si tus App Intents deben realizar navegación en la UI, deben tener acceso a todos los estados de la app que controlan las vistas. Esto solo se puede hacer con objetos compartidos globalmente como AppDependencies o singletons. Pero con las nuevas API de control de vista, puede eliminar el código de UI de las intents de la app y dejar que las vistas lo manejen por sí mismas. Usémoslo para refactorizar nuestro OpenLandmarkIntent. En primer lugar, tenemos que ajustar el intent al protocolo TargetContentProvidingIntent. En la vista SwiftUI, tengo una propiedad de ruta que se usa para modificar programáticamente el NavigationStack. Dado que está marcado como un estado, solo se puede acceder a él desde el cuerpo de la vista. Aquí es donde entra en juego el modificador de vista onAppIntentExecution. Toma el tipo de App Intent que deseas manejar y un cierre de acción con la intent pasada. En el interior, puedo hacer referencia a los parámetros de la intent la modificar la IU en consecuencia. Con este código en su lugar, podemos eliminar el código de IU de la intent en sí, o eliminar el método de ejecución por completo, así como la dependencia que ya no necesitamos. ¿No es genial?

    El sistema ejecuta el cierre de la acción poco antes de poner en primer plano la app. Por lo tanto, solo puedes leer los valores de los parámetros desde la intent. No se admite ninguna otra operación, como solicitar valores. Y si varias vistas tienen el mismo modificador, se ejecutarán todas para que cada vista pueda responder en consecuencia. Si tu app admite varias escenas o ventanas, es posible que desees controlar cuál ejecuta una intent particular. Por ejemplo, cuando estoy editando una colección en la app TravelTracking, no quisiera que navegue a un punto de referencia con esa escena. Será muy molesto. Afortunadamente, puedo controlar esto mediante las API handlesExternalEvents. Un contenido de destino que proporciona una intent tiene la propiedad contentIdentifier, cuyo valor predeterminado es el persistentIdentifier. Y normalmente es el nombre de la estructura de la intent. Siempre se puede personalizar este valor para que sea aún más específico.

    Podemos usar esto con los modificadores HandlesExternalEvents en las escenas para establecer la condición de activación, que le indica al sistema que use la escena para ejecutar la App Intent. Creará uno si aún no existe. Asegúrate de que el identificador en la matriz coincida con la propiedad del identificador de contenido de la intent que deseas manipular. Pero si las condiciones de activación son dinámicas, puedes usar el mismo modificador en las vistas. Aquí, solo permito que la escena manipule OpenLandmarkIntent si no estamos editando una colección. Puedes obtener más información sobre las escenas de SwiftUI y las condiciones de activación en estas dos sesiones. Para UIKit, puedes adaptar las intents al protocolo UISceneAppIntent, lo que les brinda la capacidad de acceder a UIScene con estos miembros. O tu delegado de escena puede responder a una ejecución de intent después de cumplir con AppIntentSceneDelegate. Por último, también puedes usar condiciones de activación para decidir qué escena manejará una App Intent. La siguiente mejora es la nueva macro ComputedProperty que te permite evitar almacenar valores en App Entities. Aquí está mi SettingsEntity que copia el defaultPlace de UserDefaults. Quiero evitar almacenar un valor duplicado en la estructura de esta manera. Más bien, debería derivarse directamente de la fuente de la verdad. Con la nueva macro ComputedProperty, puedo lograr esto accediendo a UserDefaults directamente desde el captador.

    También agregamos una nueva macro DeferredProperty para reducir el costo de crear una instancia de AppEntity. En LandmarkEntity, hay una propiedad crowdStatus cuyo valor proviene de un servidor de red, por lo que obtenerla es relativamente costoso. Quiero obtenerlo sólo si el sistema lo solicita explícitamente.

    Al marcar esta propiedad como DeferredProperty, puedo proporcionar un captador asincrónico. Dentro, puedo invocar métodos que realizan las llamadas a la red. Este captador asincrónico solo se invoca si las funcionalidades del sistema, como Atajos, lo solicitan, no cuando se crea y se pasa LandmarkEntity.

    Aquí hay algunas diferencias clave entre los tres tipos de propiedad. En general, elige ComputedProperty en lugar de Diferido debido a su menor sobrecarga del sistema. Solo recurre a la opción Diferido si esa propiedad es demasiado costosa para calcularla. Por último, pero no menos importante, ahora puedes poner tus App Intents en paquetes Swift.

    Anteriormente, habilitamos el empaquetado del código de App Intents con estructuras y bibliotecas dinámicas. Ahora puedes ponerlos en paquetes Swift y bibliotecas estáticas. Para obtener más información sobre cómo usar el protocolo AppIntentsPackage, consulta la sesión "Conoce App Intents". Espero que hayas disfrutado conocer estas funcionalidades conmigo. Como próximos pasos, prueba snippets interactivos para ver qué pueden hacer por tus intents. Asocia entidades con el contenido en pantalla para que el sistema pueda sugerirlas automáticamente al usuario. Proporciona más opciones para que las personas elijan usando la API Multiple Choice. Incorpora múltiples modos para garantizar que tus intents brinden la mejor experiencia sin importar cómo se ejecuten. Por último, para profundizar en el código, consulta nuestra app de muestra en el sitio web de Apple Developer. ¡Estoy muy emocionado de finalmente compartir todo esto contigo! No puedo esperar a ver todas las ideas creativas que se te ocurrirán.

    Eso es todo por ahora. Gracias por ver este video.

    • 4:08 - Returning a Snippet Intent

      import AppIntents
      import SwiftUI
      
      struct ClosestLandmarkIntent: AppIntent {
          static let title: LocalizedStringResource = "Find Closest Landmark"
      
          @Dependency var modelData: ModelData
      
          func perform() async throws -> some ReturnsValue<LandmarkEntity> & ShowsSnippetIntent & ProvidesDialog {
              let landmark = await self.findClosestLandmark()
      
              return .result(
                  value: landmark,
                  dialog: IntentDialog(
                      full: "The closest landmark is \(landmark.name).",
                      supporting: "\(landmark.name) is located in \(landmark.continent)."
                  ),
                  snippetIntent: LandmarkSnippetIntent(landmark: landmark)
              )
          }
      }
    • 4:31 - Building a SnippetIntent

      struct LandmarkSnippetIntent: SnippetIntent {
          static let title: LocalizedStringResource = "Landmark Snippet"
      
          @Parameter var landmark: LandmarkEntity
          @Dependency var modelData: ModelData
      
          func perform() async throws -> some IntentResult & ShowsSnippetView {
              let isFavorite = await modelData.isFavorite(landmark)
      
              return .result(
                  view: LandmarkView(landmark: landmark, isFavorite: isFavorite)
              )
          }
      }
    • 5:45 - Associate intents with buttons

      struct LandmarkView: View {
          let landmark: LandmarkEntity
          let isFavorite: Bool
      
          var body: some View {
              // ...
              Button(intent: UpdateFavoritesIntent(landmark: landmark, isFavorite: !isFavorite)) { /* ... */ }
      
              Button(intent: FindTicketsIntent(landmark: landmark)) { /* ... */ }
              // ...
          }
      }
    • 6:53 - Request confirmation snippet

      struct FindTicketsIntent: AppIntent {
      
          func perform() async throws -> some IntentResult & ShowsSnippetIntent {
              let searchRequest = await searchEngine.createRequest(landmarkEntity: landmark)
      
              // Present a snippet that allows people to change
              // the number of tickets.
              try await requestConfirmation(
                  actionName: .search,
                  snippetIntent: TicketRequestSnippetIntent(searchRequest: searchRequest)
              )
      
              // Resume searching...
          }
      }
    • 7:24 - Using Entities as parameters

      struct TicketRequestSnippetIntent: SnippetIntent {
          static let title: LocalizedStringResource = "Ticket Request Snippet"
      
          @Parameter var searchRequest: SearchRequestEntity
      
          func perform() async throws -> some IntentResult & ShowsSnippetView {
              let view = TicketRequestView(searchRequest: searchRequest)
      
              return .result(view: view)
          }
      }
    • 8:01 - Updating a snippet

      func performRequest(request: SearchRequestEntity) async throws {
          // Set to pending status...
         
          TicketResultSnippetIntent.reload()
      
          // Kick off search...
      
          TicketResultSnippetIntent.reload()
      }
    • 9:24 - Responding to Image Search

      struct LandmarkIntentValueQuery: IntentValueQuery {
      
          @Dependency var modelData: ModelData
      
          func values(for input: SemanticContentDescriptor) async throws -> [LandmarkEntity] {
              guard let pixelBuffer: CVReadOnlyPixelBuffer = input.pixelBuffer else {
                  return []
              }
      
              let landmarks = try await modelData.searchLandmarks(matching: pixelBuffer)
      
              return landmarks
          }
      }
    • 9:51 - Support opening an entity

      struct OpenLandmarkIntent: OpenIntent {
          static var title: LocalizedStringResource = "Open Landmark"
      
          @Parameter(title: "Landmark")
          var target: LandmarkEntity
      
          func perform() async throws -> some IntentResult {
              /// ...
          }
      }
    • 10:53 - Show search results in app

      @AppIntent(schema: .visualIntelligence.semanticContentSearch)
      struct ShowSearchResultsIntent {
          var semanticContent: SemanticContentDescriptor
      
          @Dependency var navigator: Navigator
      
          func perform() async throws -> some IntentResult {
              await navigator.showImageSearch(semanticContent.pixelBuffer)
      
              return .result()
          }
      
          // ...
      }
    • 11:40 - Returning multiple entity types

      @UnionValue
      enum VisualSearchResult {
          case landmark(LandmarkEntity)
          case collection(CollectionEntity)
      }a
      
      struct LandmarkIntentValueQuery: IntentValueQuery {
          func values(for input: SemanticContentDescriptor) async throws -> [VisualSearchResult] {
              // ...
          }
      }
      
      struct OpenLandmarkIntent: OpenIntent { /* ... */ }
      struct OpenCollectionIntent: OpenIntent { /* ... */ }
    • 13:00 - Associating a view with an AppEntity

      struct LandmarkDetailView: View {
      
          let landmark: LandmarkEntity
      
          var body: some View {
              Group{ /* ... */ }
              .userActivity("com.landmarks.ViewingLandmark") { activity in
                  activity.title = "Viewing \(landmark.name)"
                  activity.appEntityIdentifier = EntityIdentifier(for: landmark)
              }
          }
      }
    • 13:21 - Converting AppEntity to PDF

      import CoreTransferable
      import PDFKit
      
      extension LandmarkEntity: Transferable {
          static var transferRepresentation: some TransferRepresentation {
              DataRepresentation(exportedContentType: .pdf) {landmark in
                  // Create PDF data...
                  return data
              }
          }
      }
    • 14:05 - Associating properties with Spotlight keys

      struct LandmarkEntity: IndexedEntity {
      
          // ...
      
          @Property(indexingKey: \.displayName)
          var name: String
      
          @Property(customIndexingKey: /* ... */)
          var continent: String
      
          // ...
      }
    • 15:49 - Making intents undoable

      struct DeleteCollectionIntent: UndoableIntent {
          // ...
      
          func perform() async throws -> some IntentResult {
      
              // Confirm deletion...
      
              await undoManager?.registerUndo(withTarget: modelData) {modelData in
                  // Restore collection...
              }
              await undoManager?.setActionName("Delete \(collection.name)")
      
             // Delete collection...
          }
      }
    • 16:52 - Multiple choice

      struct DeleteCollectionIntent: UndoableIntent {
          func perform() async throws -> some IntentResult & ReturnsValue<CollectionEntity?> {
              let archive = Option(title: "Archive", style: .default)
              let delete = Option(title: "Delete", style: .destructive)
      
              let resultChoice = try await requestChoice(
                  between: [.cancel, archive, delete],
                  dialog: "Do you want to archive or delete \(collection.name)?",
                  view: collectionSnippetView(collection)
              )
      
              switch resultChoice {
              case archive: // Archive collection...
              case delete: // Delete collection...
              default: // Do nothing...
              }
          }
          // ...
      }
    • 18:47 - Supported modes

      struct GetCrowdStatusIntent: AppIntent {
      
          static let supportedModes: IntentModes = [.background, .foreground]
      
          func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog {
              if systemContext.currentMode == .foreground {
                  await navigator.navigateToCrowdStatus(landmark)
              }
      
              // Retrieve status and return dialog...
          }
      }
    • 19:30 - Supported modes

      struct GetCrowdStatusIntent: AppIntent {
          static let supportedModes: IntentModes = [.background, .foreground(.dynamic)]
      
          func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog {
              guard await modelData.isOpen(landmark) else { /* Exit early... */ }
      
              if systemContext.currentMode.canContinueInForeground {
                  do {
                      try await continueInForeground(alwaysConfirm: false)
                      await navigator.navigateToCrowdStatus(landmark)
                  } catch {
                      // Open app denied.
                  }
              }
      
              // Retrieve status and return dialog...
          }
      }
    • 21:30 - View Control

      extension OpenLandmarkIntent: TargetContentProvidingIntent {}
      
      struct LandmarksNavigationStack: View {
      
          @State var path: [Landmark] = []
      
          var body: some View {
              NavigationStack(path: $path) { /* ... */ }
              .onAppIntentExecution(OpenLandmarkIntent.self) { intent in
                  self.path.append(intent.landmark)
              }
          }
      }
    • 23:13 - Scene activation condition

      @main
      struct AppIntentsTravelTrackerApp: App {
          var body: some Scene {
              WindowGroup { /* ... */ }
      
              WindowGroup { /* ... */ }
              .handlesExternalEvents(matching: [
                  OpenLandmarkIntent.persistentIdentifier
              ])
          }
      }
    • 23:33 - View activation condition

      struct LandmarksNavigationStack: View {
          var body: some View {
              NavigationStack(path: $path) { /* ... */ }
              .handlesExternalEvents(
                  preferring: [],
                  allowing: !isEditing ? [OpenLandmarkIntent.persistentIdentifier] : []
              )
          }
      }
    • 24:23 - Computed property

      struct SettingsEntity: UniqueAppEntity {
      
          @ComputedProperty
          var defaultPlace: PlaceDescriptor {
              UserDefaults.standard.defaultPlace
          }
      
          init() {
          }
      }
    • 24:48 - Deferred property

      struct LandmarkEntity: IndexedEntity {
          // ...
      
          @DeferredProperty
          var crowdStatus: Int {
              get async throws {
                  await modelData.getCrowdStatus(self)
              }
          }
      
          // ...
      }
    • 25:50 - AppIntentsPackage

      // Framework or dynamic library
      public struct LandmarksKitPackage: AppIntentsPackage { }
      
      // App target
      struct LandmarksPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [LandmarksKitPackage.self]
          }
      }
    • 0:00 - Introducción
    • App Intents es una estructura que permite que las apps integren funcionalidades en todos los dispositivos, como Inteligencia Visual, Atajos y Spotlight. Obtén información sobre nuevos fragmentos interactivos, integración de apps en todo el sistema, experiencias de usuario refinadas y API convenientes para desarrolladores.

    • 0:55 - Fragmentos interactivos
    • Con las actualizaciones más recientes, ahora puedes mejorar tus apps con fragmentos interactivos. Estos fragmentos son vistas dinámicas en las que se muestra información personalizada basada en App Intents. Por ejemplo, una app de jardinería puede sugerir encender los aspersores, o una app de pedidos de comida puede permitir a las personas configurar sus pedidos antes de realizarlos. La app de muestra TravelTracking es un excelente ejemplo. Cuando un usuario busca el punto de referencia más cercano, aparece un fragmento interactivo. Este fragmento incluye el nombre del punto de referencia y un botón con forma de corazón. Al tocar el botón del corazón, la persona puede marcar como favorito el punto de referencia y el fragmento se actualiza al instante para reflejar el nuevo estado, todo sin salir de la vista actual. Esta interactividad se consigue mediante el nuevo protocolo “SnippetIntent”. Puedes crear fragmentos de resultados en los que se muestre información después de una acción, y estos fragmentos pueden incluir botones o conmutadores que activen otras Intents de la app. Por ejemplo, en la app TravelTracking, el botón de corazón ejecuta la Intent Actualizar favoritos y puedes agregar un botón Buscar boletos para iniciar otra Intent. Cuando alguien interactúa con estos botones, el sistema ejecuta la Intent correspondiente y el fragmento se actualiza en consecuencia. El sistema garantiza que los datos estén siempre actualizados al obtener entidades de la app a partir de consultas cada vez que se actualiza el fragmento. Este proceso es fluido y animado, lo que proporciona una experiencia de usuario óptima. También puedes crear fragmentos de confirmación que soliciten a las personas que proporcionen información adicional antes de continuar. Por ejemplo, en la app TravelTracking, cuando alguien toca Buscar boletos, aparece un fragmento de confirmación que pregunta cuántos boletos desea buscar. Luego, la persona puede interactuar con este fragmento y el sistema actualiza la vista en tiempo real en función de sus entradas.

    • 8:15 - Nuevas integraciones de sistemas
    • App Intents en iOS 26 mejora la integración del sistema, lo que permite la búsqueda de imágenes directamente desde capturas de cámara o capturas de pantalla. Las apps ahora pueden mostrar los resultados de búsqueda en el panel de búsqueda del sistema. Para respaldar esta capacidad, implementa consultas que se ajusten al protocolo “IntentValueQuery” y procesen datos “SemanticContentDescriptor” para devolver conjuntos de entidades de la app. Cuando alguien toca un resultado, se activa la “OpenIntent” correspondiente, que abre la app en la página correspondiente. Las “OpenIntents” no se limitan a la búsqueda de imágenes y también puedes usarlas en Spotlight. Considera optimizar el rendimiento de la búsqueda, devolver varias páginas de resultados y permitir a los usuarios continuar la búsqueda dentro de la app. Más allá de la búsqueda de imágenes, las App Intents habilitan entidades en pantalla, lo que permite a las personas interactuar con Siri y ChatGPT sobre el contenido visible en la app. Puedes asociar entidades con vistas, adaptarlas al protocolo “Transferable” y admitir varios tipos de datos, como PDF, texto simple y texto enriquecido. Para mejorar la búsqueda en Spotlight, haz que las entidades de la app se ajusten a “IndexedEntity”, dónalas a Spotlight y anota el contenido en pantalla con entidades. La implementación de “PredictableIntent” permite que el sistema aprenda del comportamiento del usuario y proporcione sugerencias personalizadas.

    • 15:01 - Mejoras en la experiencia de usuario
    • Las nuevas funcionalidades de la plataforma de desarrollo de apps mejoran las experiencias de los usuarios a través de una funcionalidad mejorada de deshacer, opciones de opción múltiple y modos compatibles. El protocolo “UndoableIntent” permite a las personas revertir acciones realizadas con App Intents usando gestos familiares, lo que proporciona una red de seguridad para la experimentación. Puedes implementar esto adaptando las Intents al protocolo y registrando acciones de deshacer con el administrador de deshacer. Con la API de opción múltiple, puedes presentar a las personas varias opciones para una Intent, en lugar de solo una confirmación binaria. También puedes personalizarla con un diálogo y una vista SwiftUI personalizada. Los modos compatibles brindan a las Intents un mayor control sobre la puesta en primer plano de la app. Esta capacidad permite que las Intents se comporten de forma diferente en función de cómo interactúe una persona con el dispositivo. Por ejemplo, una Intent puede proporcionar información solo de voz cuando la persona está conduciendo, pero llevarla directamente a la app cuando está mirando el dispositivo. Puedes especificar los modos admitidos para sus Intents y usar la propiedad “currentMode” para verificar qué modo está activo.

    • 21:02 - API de conveniencia
    • Con las nuevas API de control de vista en SwiftUI, puedes refactorizar las Intents de la app eliminando el código de IU y permitiendo que las vistas manejen la navegación directamente. Usa el modificador de vista “onAppIntentExecution”, que permite que las vistas respondan a app intents específicas y modifiquen la IU en consecuencia. El sistema ejecuta el cierre de la acción poco antes de poner en primer plano la app, y varias vistas pueden responder a la misma Intent. Puedes controlar qué escena maneja una Intent mediante las API “handlesExternalEvents”, lo que garantiza una navegación adecuada al contexto. Además, nuevas macros como “ComputedProperty” y “DeferredProperty” optimizan “AppEntities”, lo que reduce los costos de almacenamiento e instanciación. Las App Intents ahora también se pueden empaquetar en paquetes de Swift, lo que proporciona mayor flexibilidad y reutilización.

Developer Footer

  • Videos
  • WWDC25
  • Explora nuevos avances en App Intents
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines