Actualizado: .

Dynamic Island resulta útil cuando responde a una pregunta breve: cuándo llegará el coche, cuánto falta para que termine el temporizador o qué ocurre con el entrenamiento. Para que esta función sea fiable hacen falta un diseño compacto, ActivityKit, una extensión de widgets, actualizaciones correctas, gestión de acciones y una finalización clara. Aquí recorremos todo el camino, desde el caso de uso hasta la verificación de la implementación.

Una persona pide comida y abre sus mensajes del trabajo. En la parte superior de la pantalla le basta con ver «4 min». Al expandirla, necesita saber que el repartidor está en la entrada del patio. En la pantalla bloqueada debe reconocer el mismo pedido. Tras la entrega, el estado debe finalizar. Esta es la secuencia que diseñamos: un proceso, varias vistas y ninguna promesa que los datos ya no respalden.

Verificado con la documentación de Apple el 5 de noviembre de 2026. Esta es una guía práctica con diagramas propios, no un informe de pruebas de todos los iPhone. Los ejemplos de Swift muestran partes de la integración; no constituyen una aplicación compilada y firmada. Hay que comprobar la disponibilidad de cada API en el SDK de Xcode y en el deployment target del proyecto.

1. Dynamic Island, Live Activity y widget: tres conceptos diferentes

Dynamic Island es el lugar de visualización. Live Activity es la representación de una actividad en curso. ActivityKit es el mecanismo que gestiona su ciclo de vida. La interfaz se crea con WidgetKit y SwiftUI en una extensión de la aplicación. Un widget convencional, aunque use el mismo WidgetKit, se actualiza con otro contrato: su timeline no sustituye el ciclo de vida de una Live Activity. Esta diferencia se explica en la guía de Apple para integrar Live Activities.

Onda de audio y tiempo de una grabación de Notas de Voz en Dynamic Island del iPhone
La guía de Apple muestra cómo una grabación de Notas de Voz permanece visible en Dynamic Island sobre la pantalla de inicio. Apple.
Live Activity de entrega en la pantalla bloqueada del iPhone: tres artículos que llegan en ocho minutos
Ejemplo de las Human Interface Guidelines de Apple: la misma entrega tiene una presentación propia en la pantalla bloqueada. Apple.
MecanismoResponsabilidadEjemplo
ActivityKitCrear, actualizar y finalizar una actividadEntrega desde el inicio del seguimiento hasta su finalización
WidgetKit + SwiftUIDefinir las vistas y las interacciones admitidasETA breve y tarjeta expandida
Dynamic IslandZona de visualización del sistema en un dispositivo compatibleEstados compacto, mínimo y expandido
Widget convencionalInformación periódica y acceso rápidoPlan del día, próximo evento

Esto tiene una consecuencia importante: dar soporte a iPhone sin isla no es trabajo inútil. La pantalla de bloqueo sigue siendo un contexto independiente. Y dibujar una cápsula dentro de la aplicación no conecta el producto con la interfaz del sistema.

2. Acordad primero qué sigue exactamente la persona

Para una entrega, definid cuándo empieza el seguimiento, los cambios de fase y el final. «Pedido aceptado» quizá aún no requiera atención constante. «Repartidor en camino» ya responde a una pregunta recurrente. «El repartidor espera» exige un cambio visible. «Entregado» finaliza el proceso; la tarjeta no debe convertirse en una invitación a volver a pedir.

En un entrenamiento, la pregunta principal es distinta: si sigue grabándose, cuánto tiempo ha pasado y cómo pausarlo. En un partido, el marcador y el periodo actual. En un temporizador, el tiempo restante. Son modelos de contenido diferentes, aunque encajen en una forma parecida.

La generación con IA tampoco obtiene automáticamente derecho a ocupar la isla. Si el resultado puede consultarse más tarde, suelen bastar una notificación al terminar y una pantalla de historial. Si el usuario sigue deliberadamente una tarea breve en curso, evaluad la vista en directo por su utilidad real. Nunca mostréis un porcentaje inventado para conseguir una animación bonita.

Antes del diseño, escribid una frase: «Mientras ocurre el proceso, la persona quiere conocer la respuesta y, a veces, realizar una acción; la vista desaparece bajo esta condición». Si falta la última parte, probablemente habéis elegido el mecanismo equivocado.

3. Cuatro diseños principales y una prioridad informativa

Cuatro vistas propias de una entrega: compact con cuatro minutos, minimal con un icono, expanded con el contexto del repartidor y Lock Screen con la fase del pedido.
Diagrama de jerarquía informativa. Diseños esquemáticos, no medidas exactas ni capturas de iOS.

En el estado compacto, conservad el indicador principal. Minimal debe seguir siendo reconocible junto a otra actividad. Expanded puede añadir contexto y una acción. La pantalla de bloqueo necesita información suficiente para entender la tarea sin abrir la aplicación. El sistema determina tamaños, zonas y reglas de expansión; hay que contrastarlos con las Human Interface Guidelines para Live Activities.

Dynamic Island expandida con un pedido, la hora de llegada y un botón para contactar al repartidor
Primer plano oficial de Apple: Dynamic Island expandida añade detalles de la entrega y la acción de contacto. La zona roja señala el área de la cámara en la ilustración didáctica de Apple. Apple Human Interface Guidelines.

Por ejemplo: en compacto, «4 min»; en mínimo, el símbolo de entrega; en expandido, «Repartidor en la entrada del patio»; en bloqueo, la fase y su relación con un pedido concreto. No reduzcáis una pantalla grande a cuatro tamaños. Seleccionad de nuevo la información para cada contexto.

Tened en cuenta el ancho limitado de la isla, varios procesos simultáneos, el texto grande, las traducciones largas y VoiceOver. El número «4» sin nombre accesible es ambiguo. «El repartidor llegará aproximadamente en cuatro minutos» explica su significado. Los detalles privados del pedido no deben quedar visibles simplemente porque el teléfono esté sobre la mesa.

4. Preparación del proyecto y modelo de datos

Partid del target de iOS existente. Añadid o reutilizad una Widget Extension, incluid la configuración de Live Activity en el conjunto de widgets de la extensión y estableced NSSupportsLiveActivities = YES en la aplicación. Comprobad la firma, los identificadores, las versiones de destino y la disponibilidad del tipo de datos compartido para la aplicación y la extensión. Un sitio web o un contenedor JavaScript no crean ese target por sí solos.

ActivityAttributes separa los atributos constantes del ContentState mutable. El número de pedido permanece constante; la fase y la previsión de llegada cambian. En el ejemplo mínimo no transmitimos la dirección, datos personales, imágenes ni el modelo completo del servidor:

import ActivityKit

struct DeliveryAttributes: ActivityAttributes {
    struct ContentState: Codable, Hashable {
        var phase: String
        var etaMinutes: Int?
    }
    let orderID: String
}

etaMinutes puede ser desconocido. El valor nil es aquí más honesto que cero: «sin previsión» no significa «el repartidor ya ha llegado». En una aplicación real conviene describir las fases con un conjunto de valores acordado, para que backend y cliente compartan el mismo significado.

Comprobad ActivityAuthorizationInfo().areActivitiesEnabled. La configuración de notificaciones convencionales y la autorización para Live Activities no son intercambiables. Los cambios pueden observarse mediante activityEnablementUpdates; se explica en la API de configuración de ActivityKit. Incluso después de una comprobación positiva, la creación puede fallar, por ejemplo, por límites del sistema.

enum DeliveryActivityError: Error { case disabled }

@available(iOS 16.2, *)
func startDelivery(orderID: String) throws
    -> Activity<DeliveryAttributes> {
    guard ActivityAuthorizationInfo().areActivitiesEnabled else {
        throw DeliveryActivityError.disabled
    }
    let state = DeliveryAttributes.ContentState(
        phase: "on_the_way", etaMinutes: 4
    )
    return try Activity<DeliveryAttributes>.request(
        attributes: DeliveryAttributes(orderID: orderID),
        content: ActivityContent(
            state: state,
            staleDate: .now.addingTimeInterval(120)
        ),
        pushType: nil
    )
}

La función devuelve intencionadamente el error al código que la invoca. El producto debe mostrar el resultado de la creación en su interfaz habitual. El ejemplo utiliza ActivityContent, disponible desde iOS 16.2, y actualizaciones locales sin push. La variante con servidor requiere la configuración adicional que se describe más adelante.

5. Cómo conectar los datos con Dynamic Island

En ActivityConfiguration se define la vista convencional y, en DynamicIsland , las regiones compactas y la vista expandida. El siguiente fragmento muestra precisamente la estructura. La vista DeliverySummary es un componente de vuestra aplicación que debéis implementar; no es una clase de Apple.

ActivityConfiguration(for: DeliveryAttributes.self) { context in
    DeliverySummary(state: context.state, isStale: context.isStale)
} dynamicIsland: { context in
    DynamicIsland {
        DynamicIslandExpandedRegion(.bottom) {
            DeliverySummary(state: context.state, isStale: context.isStale)
        }
    } compactLeading: {
        Image(systemName: "shippingbox")
    } compactTrailing: {
        if !context.isStale, let eta = context.state.etaMinutes {
            Text("\(eta) min")
        } else {
            Text("—")
        }
    } minimal: {
        Image(systemName: "shippingbox")
    }
}

En el código real, DeliverySummary debe gestionar explícitamente isStale y la ausencia de ETA. Para la navegación, añadid widgetURLcon el destino del pedido correspondiente. Al abrirlo, comprobad la cuenta actual y el acceso al objeto: un enlace público no equivale a autorización. No enviéis a la persona a la pantalla de inicio para buscar el pedido que acaba de pulsar.

6. Botones: App Intents y un resultado confirmado

Las vistas interactivas compatibles utilizan Button(intent:) y Toggle con un App Intent. El intent correspondiente llama al mismo comando de dominio que la pantalla principal. Apple explica el contexto de ejecución en la guía de widgets y Live Activities interactivos; para las operaciones relacionadas con la actividad existe LiveActivityIntent.

Al pausar un entrenamiento, la secuencia es: pulsación → comprobación de la sesión actual → comando para pausar la grabación → confirmación → nuevo estado. Si el comando se envía a otro dispositivo o servidor, no es un cambio instantáneo y local de texto. Una segunda pulsación no debe reanudar el entrenamiento por accidente: «establecer pausa» es más fiable que «alternar estado» a ciegas.

Para una entrega suele bastar con abrir los detalles o contactar con el repartidor. Cancelar un pedido, abrir una cerradura y otras acciones sensibles necesitan su propio flujo de confirmación y autenticación del sistema cuando corresponda. Un botón pequeño no reduce las consecuencias de la operación.

7. Actualizaciones del servidor: tres tokens que no deben confundirse

Cuando los datos llegan del servidor, activad la capacidad Push Notifications y cread la actividad con pushType: .token. Después observad activity.pushTokenUpdates: el token aparece de forma asíncrona y puede cambiar. El backend lo vincula a una actividad y una cuenta concretas. No lo guardéis en registros públicos.

TokenFinalidadError que evitar
Device token convencional de APNsNotificaciones remotas convencionales de la aplicaciónUsarlo en lugar del update token de ActivityKit
Update token de la actividadActualizar o finalizar una Live Activity concretaVincular un único token a todos los pedidos
Push-to-start tokenIniciar remotamente una nueva actividad en un runtime compatibleEnviar una actualización convencional o ignorar la rotación

Para actualizar una actividad concreta, la solicitud APNs utiliza apns-push-type: liveactivity y un topic con el formato <app-bundle-id>.push-type.liveactivity. El contenido de content-state debe coincidir con el modelo Swift. Este es un ejemplo propio de cómo construir una actualización en JavaScript:

const now = Math.floor(Date.now() / 1000);
const payload = {
  aps: {
    timestamp: now,
    event: "update",
    "content-state": { phase: "on_the_way", etaMinutes: 4 },
    "stale-date": now + 120
  }
};

Aquí, los 120 segundos son el periodo de vigencia elegido para el pedido de ejemplo, no un límite de Apple. En el servidor, el tiempo y la vigencia de los datos deben proceder de la lógica del producto. Las marcas de tiempo del sobre APNs usan segundos Unix; eso no justifica codificar cualquier Date de Swift dentro del modelo propio con ese mismo formato. Comprobad la decodificación real.

El contrato completo de start/update/end, prioridades y canales se describe en la documentación de push de ActivityKit. Para un evento deportivo compartido puede utilizarse un canal broadcast en versiones compatibles; no agrupéis sin criterio los pedidos personales en una audiencia común. Frequent Updates tiene una configuración y un presupuesto independientes; no promete actualizaciones cada segundo. La aceptación de una solicitud APNs no demuestra que se haya mostrado ni visto.

8. Vigencia, finalización y límites reales

Un cambio local confirmado se transmite al objeto de actividad existente mediante update. En este ejemplo, un ETA de cero es válido porque sabemos que el repartidor ya está esperando, no porque se haya perdido la previsión.

let nextState = DeliveryAttributes.ContentState(
    phase: "courier_waiting", etaMinutes: 0
)
await activity.update(
    ActivityContent(
        state: nextState,
        staleDate: .now.addingTimeInterval(120)
    )
)
Ciclo de vida: inicio, respuesta vigente, datos obsoletos, nueva respuesta confirmada y finalización. Eliminar la tarjeta no cancela el pedido.
La fiabilidad de los datos y la visibilidad de la tarjeta cambian de forma independiente. Ejemplo didáctico de una entrega.

La fiabilidad de la respuesta importa más que su presencia continua. Si no llega una actualización del ETA, no sigáis prometiendo con seguridad los cuatro minutos anteriores. staleDate marca el momento en que los datos caducan; context.isStale permite cambiar la interfaz. El campo no escribe «Datos desactualizados» por el desarrollador ni actualiza la previsión.

Diferenciad cuatro situaciones: el pedido sigue en curso; los datos han caducado; la tarjeta está oculta; el comando del usuario no está confirmado. Pueden coexistir. Quitar la Live Activity de la pantalla de bloqueo no cancela la entrega. Al reiniciar la aplicación, consultad Activity<DeliveryAttributes>.activitiesen lugar de crear una nueva copia de cada tarea.

Para las Live Activities estándar, la documentación actual indica hasta ocho horas de vida activa, seguidas de hasta cuatro horas adicionales en la pantalla de bloqueo, y un presupuesto conjunto de 4 KB para datos estáticos y dinámicos. La extensión de renderizado no obtiene acceso arbitrario a la red ni actualizaciones de ubicación. Estas restricciones de duración y datos deben considerarse antes de elegir el caso de uso.

let finalState = DeliveryAttributes.ContentState(
    phase: "delivered", etaMinutes: nil
)
await activity.end(
    ActivityContent(state: finalState, staleDate: nil),
    dismissalPolicy: .default
)

Al finalizar, enviad el contenido final. La política .default permite que el sistema muestre el resultado durante un tiempo en la pantalla de bloqueo; .immediate lo retira inmediatamente; .after(...) establece un aplazamiento limitado. La elección debe tener sentido: la persona puede necesitar ver el estado final, pero una tarjeta permanente de un trabajo terminado genera una expectativa falsa.

9. Apple Watch, StandBy, Mac y CarPlay

La isla no es el único punto de uso. Para Apple Watch y CarPlay se puede preparar una vista pequeña mediante supplementalActivityFamilies([.small]) y elegir el contenido según activityFamily. La guía de vistas adicionales también explica la adaptación a otros tamaños y al ancho limitado de Dynamic Island.

Apple Watch Series 10 con tarjetas de Smart Stack en watchOS 11
Imagen oficial del Apple Watch Series 10 con watchOS 11: Smart Stack muestra tarjetas en la muñeca. Es un ejemplo de una versión concreta del sistema. Apple.

Una diferencia esencial: la interactividad en Apple Watch no implica interactividad en CarPlay. El sistema desactiva los botones e interruptores de Live Activity en el coche. Por eso, «El repartidor espera» debe seguir siendo útil sin un botón al lado. Recibir una Live Activity en el reloj o en el menú del Mac tampoco crea una aplicación autónoma de watchOS o macOS.

StandBy exige legibilidad a distancia; un valor grande y una fase clara funcionan mejor que un párrafo largo. En el reloj hacen falta etiquetas breves y áreas de pulsación cómodas. En el Mac, considerad el acceso a la tarea y la dependencia del iPhone enlazado. La compatibilidad debe comprobarse para la combinación concreta de versiones y ajustes.

La documentación actual incluye actividades programadas y transient. Una actividad transient está pensada para una interacción breve en la isla expandida y termina al salir de ese contexto; no sustituye el seguimiento convencional en segundo plano. Antes de usar nuevas sobrecargas de request , comprobad la disponibilidad del símbolo concreto y el estado de publicación del SDK, no solo el nombre de la familia ActivityKit.

10. Las pruebas que distinguen un prototipo de una integración

CondiciónResultado esperado
Live Activities desactivadasLa tarea principal sigue disponible; el sistema no promete la isla
Sin Dynamic IslandSe ha comprobado la vista necesaria en la pantalla de bloqueo
Dos actividadesCada una se identifica con claridad y los enlaces no se confunden
Datos desactualizadosEl ETA no parece confirmado; la pérdida de vigencia es visible
El usuario elimina la tarjetaEl pedido no se cancela; no se restaura por la fuerza
La red vuelve después de finalizarUn evento antiguo no reactiva la tarea terminada
Cambia el token / APNs devuelve un errorEl backend actualiza el registro y distingue entrega de aceptación de la solicitud
La actividad finalizaSe muestra el resultado, se detienen las actualizaciones y se aplica la política de retirada

Añadid pruebas específicas de traducciones largas, texto grande, VoiceOver, visualización oscura o atenuada y acceso a la aplicación tras cambiar de cuenta. Para pausar o cancelar, comprobad la repetición del comando y las acciones simultáneas desde otro dispositivo.

El simulador sirve para los diseños, pero no confirma la entrega APNs, el comportamiento de un iPhone concreto ni la transferencia al reloj. En el informe diferenciad: compilado; mostrado en simulador; probado en dispositivo; probado con servidor. Probad el cierre forzado por separado de la simple minimización.

11. Qué debe entregar el equipo

El resultado mínimo completo es un caso de uso justificado, un modelo de estado acordado, una extensión con las vistas necesarias, inicio/actualización/finalización, un enlace preciso a la tarea y un recorrido verificado cuando Live Activities no esté disponible. El transporte desde el servidor y la interactividad se añaden cuando el caso realmente los necesita.

Ejemplo de criterio de aceptación: «Tras empezar el viaje, la persona ve una previsión de llegada vigente; si se pierden datos, la interfaz deja de prometer un ETA exacto; al pulsar se abre el viaje correcto; al finalizar se detienen las actualizaciones; una actividad eliminada por el usuario no reaparece para reclamar su atención». Esto es mucho más sólido que pedir «una isla bonita».

Aplicar la guía con Codex y Claude Code

Esta investigación nos llevó a crear skills abiertos para Codex y Claude Code que analizan un proyecto y proponen mejoras útiles para su sitio web o aplicación. Interaction Surfaces ayuda a descubrir superficies e interacciones adecuadas. Las propuestas son opcionales: el usuario elige qué escenarios aportan valor y cuáles quiere implementar.

Para trabajar en vuestra aplicación podéis instalar el skill Interaction Surfaces desde GitHub. Incluye la configuración de ActivityKit y WidgetKit, un ejemplo Swift, App Intents, APNs y una guía específica para Apple Watch. El paquete en inglés abarca toda la serie; las instrucciones de instalación y un ejemplo de invocación están en el artículo general.

Preguntas frecuentes

¿Se puede activar Dynamic Island desde un sitio web convencional?

Un sitio puede ofrecer una notificación o un enlace a la tarea, pero no tiene acceso directo a ActivityKit. Hace falta una integración nativa de la aplicación; instalar una PWA no sustituye la extensión de widgets.

¿Hace falta un servidor independiente para cada temporizador?

No. Para un intervalo local conocido, considerad primero la representación del tiempo que ofrece el sistema. El servidor hace falta cuando los cambios proceden de una fuente remota; enviar push cada segundo para mover los números no resuelve una necesidad del producto.

¿Se puede garantizar que la actividad aparezca en la isla?

No. Los ajustes, el dispositivo, otras actividades y las decisiones del sistema influyen en la visualización. Diseñad todo el recorrido de la tarea, sin depender de una sola cápsula visible.

¿Puede copiarse el código del artículo sin cambios?

No. Explica los límites entre componentes. Para publicar hacen falta modelos reales, vistas, gestión de errores, firma, enrutamiento, comprobación de API y pruebas del proyecto.