Apple ha incorporado en iOS 27 un nuevo framework llamado MediaIntelligence, pensado para entender conjuntos de imágenes y el contenido temporal de los vídeos directamente en el dispositivo. A primera vista puede parecer una nueva capa del framework Vision, pero su propósito es diferente: Vision produce observaciones sobre una imagen o un fotograma; Media Intelligence conserva resultados, relaciona elementos entre distintos recursos y responde a preguntas sobre una colección completa o una línea temporal.
Antes de entrar en sus APIs conviene tener presente que el framework continúa en beta. Sus nombres, requisitos y comportamiento pueden cambiar durante el ciclo de iOS 27, por lo que no resulta prudente construir todavía una capa de dominio demasiado acoplada a sus tipos. Encapsularlo tras protocolos propios facilitará adaptar cualquier modificación de las próximas versiones de Xcode sin extenderla por toda la aplicación.
El framework expone por ahora dos piezas principales. FaceGroupAnalyzer detecta rostros, persiste los resultados y agrupa las apariciones que parecen pertenecer a una misma persona. VideoAnalyzer, por su parte, ejecuta solicitudes como KeyFrameAnalysisRequest y HighlightAnalysisRequest para localizar fotogramas representativos y fragmentos relevantes dentro de un vídeo.
La diferencia con Vision está en el nivel de abstracción. Vision sigue siendo la opción adecuada para OCR, puntos de referencia de caras o posturas humanas, segmentación, saliencia o el procesamiento personalizado de cada fotograma. Media Intelligence entra en juego cuando la aplicación necesita responder a cuestiones como «qué imágenes contienen a la misma persona» o «qué partes de este vídeo representan mejor su contenido». No reemplaza el análisis visual de bajo nivel, sino que resuelve parte de la persistencia, la comparación y la agregación temporal que antes tenía que implementar cada aplicación.
Agrupar rostros a través de una colección
FaceGroupAnalyzer combina cuatro acciones que normalmente exigirían componentes separados: incorpora imágenes, detecta sus rostros, conserva los resultados y crea grupos de apariciones similares. Su carácter persistente es una de las novedades más importantes. Al inicializarlo hay que proporcionarle un directorio de escritura y reutilizar ese mismo directorio entre lanzamientos.
import MediaIntelligence
@available(iOS 27.0, *)
func makeConferenceAnalyzer() throws -> FaceGroupAnalyzer {
let directory = URL.applicationSupportDirectory
.appending(path: "ConferenceFaceIndex", directoryHint: .isDirectory)
try FileManager.default.createDirectory(
at: directory,
withIntermediateDirectories: true
)
return try FaceGroupAnalyzer(workingDirectory: directory)
}
Application Support encaja bien para este almacenamiento porque no se trata de una caché desechable: el índice permite continuar el trabajo en posteriores ejecuciones y evita volver a analizar toda la colección. El contenido de ese directorio pertenece exclusivamente al framework. La aplicación no debe colocar allí sus propios ficheros, editar la base de datos interna ni usar su estructura como una API implícita.
Cada imagen se representa mediante MediaIntelligenceImageAsset y recibe un identificador definido por la aplicación. Este identificador debe corresponder con la identidad lógica del recurso y permanecer estable. Si se genera un UUID distinto en cada ejecución, el analizador interpretará una misma imagen como un recurso nuevo y el índice crecerá innecesariamente.
import Foundation
import MediaIntelligence
struct SessionPhoto {
let recordID: String
let fileURL: URL
}
@available(iOS 27.0, *)
func makeAssets(from photos: [SessionPhoto]) -> [MediaIntelligenceImageAsset] {
photos.map { photo in
MediaIntelligenceImageAsset(
id: MediaIntelligenceImageAsset.ID(photo.recordID),
kind: .url(photo.fileURL)
)
}
}
La llamada a insertOrUpdateAssets(_:) no devuelve un array convencional, sino una secuencia asíncrona. El analizador puede entregar el resultado de cada imagen según termina de procesarla, lo que permite actualizar el progreso, guardar resultados parciales o respetar la cancelación sin tener que esperar a que concluya el procesamiento de todo el lote.
@available(iOS 27.0, *)
func detectFaces(
in assets: [MediaIntelligenceImageAsset],
using analyzer: FaceGroupAnalyzer
) async throws -> Int {
let results = try await analyzer.insertOrUpdateAssets(assets)
var faceCount = 0
for try await (_, faces) in results {
try Task.checkCancellation()
faceCount += faces.count
}
return faceCount
}
En esta primera fase cada rostro dispone de un id, un assetID y unos límites normalizados en bounds, pero todavía no representa a una persona agrupada. El id identifica una aparición concreta en una imagen. El entityID, en cambio, vincula varias apariciones que el sistema considera pertenecientes a la misma persona y se asigna durante la fase posterior de agrupación.
Un proceso deliberadamente dividido en dos fases
Añadir, actualizar o eliminar imágenes deja el índice en estado .stale. El análisis de rostros ya está almacenado, pero las relaciones entre ellos necesitan recalcularse. Cuando el analizador se encuentra en ese estado, la aplicación debe llamar a update() para construir de nuevo los grupos.
@available(iOS 27.0, *)
func updateGroupsIfNeeded(_ analyzer: FaceGroupAnalyzer) async throws {
if await analyzer.state == .stale {
try await analyzer.update()
}
}
El estado puede ser .ready, cuando las asignaciones están actualizadas; .stale, cuando hay cambios pendientes de agrupar; o .updating, mientras el algoritmo está trabajando. Esta separación es útil para colecciones grandes porque incorporar contenido y reorganizar todos los grupos no tienen que ser una única operación indivisible. Si la aplicación termina durante una actualización, los rostros ya almacenados se conservan y el estado vuelve a .stale en el siguiente lanzamiento, de modo que el proceso puede reanudarse.
Una vez actualizado el índice, allFacesByEntityID permite recorrer todas las apariciones organizadas por persona. También existen consultas más concretas, como fetchFaces(for:) y fetchAssetIDs(for:). El framework devuelve identidades técnicas, no nombres reales: relacionar un entityID con «Ana» o «ponente principal» continúa siendo responsabilidad de la aplicación y debería almacenarse fuera del directorio administrado por el analizador.
identifyFaces(in:) cubre un caso especialmente interesante: comparar imágenes nuevas con los grupos existentes sin incorporarlas al almacén persistente. Puede servir para previsualizar una importación, sugerir una clasificación antes de guardarla o filtrar una captura temporal. Para obtener la mejor precisión conviene ejecutarlo con el índice en estado .ready, aunque la API también puede trabajar cuando está .stale.
Un grupo no equivale a una identidad verificada. La beta pública no proporciona una puntuación numérica de confianza para cada asignación y el sistema puede separar a una persona en varios grupos o fusionar rostros parecidos. Una aplicación real necesita permitir corregir, unir, dividir y nombrar grupos. Presentar el resultado como una certeza biométrica sería una interpretación más fuerte de lo que la API garantiza.
Encontrar los mejores momentos de un vídeo
VideoAnalyzer aplica una arquitectura diferente. No mantiene un catálogo persistente de personas, sino que recibe un MediaIntelligenceVideoAsset y una o varias solicitudes de análisis. KeyFrameAnalysisRequest identifica fotogramas representativos y HighlightAnalysisRequest devuelve segmentos destacados de la línea temporal.
import MediaIntelligence
@available(iOS 27.0, *)
func analyzeTrainingVideo(at url: URL) async throws {
let video = MediaIntelligenceVideoAsset(url: url)
let (keyFrames, highlights) = try await VideoAnalyzer.shared.analyze(
video,
for: KeyFrameAnalysisRequest(),
HighlightAnalysisRequest()
)
// La aplicación decide cómo generar miniaturas,
// reproducir los intervalos y presentar los resultados.
print(keyFrames, highlights)
}
El resultado no es un editor de vídeo terminado. Media Intelligence selecciona momentos y rangos temporales, mientras que la aplicación sigue siendo responsable de extraer miniaturas, reproducir el contenido, montar una previsualización o exportar un nuevo recurso mediante otro framework como AVFoundation. Esa división mantiene a esta librerías con el foco en la decisión semántica y deja el tratamiento de los datos multimedia en las APIs especializadas que ya existen.
Con Vision sería posible analizar fotogramas de forma individual, pero la aplicación tendría que decidir cada cuánto muestrear, comparar los resultados a lo largo del tiempo y crear su propia heurística para determinar qué fragmentos son importantes. VideoAnalyzer ofrece directamente decisiones a escala de la línea temporal, que es precisamente el trabajo adicional que justifica este nuevo framework.
Concurrencia, dispositivo y ciclo de vida
El análisis puede ser costoso y no debería bloquear MainActor. Un modelo observable puede permanecer aislado al actor principal para publicar el estado de la interfaz, pero el procesamiento debe ejecutarse fuera de él y regresar con resultados preparados para una única actualización. En Swift 6.2 y posteriores, @concurrent puede expresar esa intención cuando una función hereda el aislamiento del tipo que la contiene.
import Observation
@MainActor
@Observable
final class MediaImportModel {
private(set) var importedFaces = 0
private(set) var isAnalyzing = false
func importAssets(_ assets: [MediaIntelligenceImageAsset]) async throws {
isAnalyzing = true
defer { isAnalyzing = false }
importedFaces = try await runAnalysis(assets)
}
@concurrent
private func runAnalysis(
_ assets: [MediaIntelligenceImageAsset]
) async throws -> Int {
let analyzer = try makeConferenceAnalyzer()
let count = try await detectFaces(in: assets, using: analyzer)
try await updateGroupsIfNeeded(analyzer)
return count
}
}
Las pruebas del pipeline requieren un dispositivo físico compatible. El simulador puede mostrar la interfaz circundante, pero no reproduce el procesamiento respaldado por el hardware necesario y la creación del contexto puede fallar. Tampoco existe una propiedad pública genérica equivalente a isNeuralEngineAvailable que elimine todos los casos de error. La interfaz debe contemplar el fallo de inicialización o análisis como un estado posible más, con una explicación y una forma de reintentar.
Mover una función fuera de MainActor no la convierte en una tarea de larga duración del sistema. Una Task normal puede cancelarse cuando desaparece la vista y no tiene garantizado continuar después de que iOS suspenda la aplicación. En catálogos grandes conviene procesar lotes idempotentes, persistir el progreso y diseñar la reanudación desde el principio. También hay que probar cancelaciones, cierres repentinos, relanzamientos y situaciones de falta almacenamiento y/o otros recursos.
Privacidad y eliminación de datos
Apple ejecuta la detección y agrupación en el dispositivo, sin necesidad de enviar los rostros a un servidor ni disponer de conexión de red. Esta arquitectura mejora la privacidad y reduce la exposición de datos sensibles, pero no sustituye las responsabilidades de producto: la aplicación debe explicar por qué analiza personas, limitar el uso de los resultados y ofrecer controles para eliminarlos.
FaceGroupAnalyzer proporciona varios niveles de borrado. deleteAssets(_:) elimina recursos concretos, deleteAllAssets() vacía los elementos indexados manteniendo el almacén, y FaceGroupAnalyzer.purge(workingDirectory:) elimina permanentemente los datos, las agrupaciones y los metadatos gestionados dentro del directorio. El último es el mecanismo apropiado para una eliminación total, no una operación de mantenimiento rutinaria.
Conviene mantener los nombres asignados por el usuario y el resto de metadatos de negocio en un almacén propio. Así se evita depender de la implementación privada del framework, se puede borrar o reconstruir el índice sin perder información ajena a él y resulta más sencillo aplicar políticas distintas de conservación.
Cuándo merece la pena adoptarlo
Media Intelligence encaja en aplicaciones que trabajan con galerías de eventos, archivos audiovisuales, diarios de viaje, catálogos privados o herramientas de edición que necesitan organizar personas y generar previsualizaciones automáticamente. Para analizar una sola imagen, obtener texto o detectar una pose, Vision continúa siendo una solución más directa y controlable.
Su principal valor no es una nueva detección de caras, sino convertir en una API del sistema todo el trabajo que rodea al análisis: identidades estables, almacenamiento incremental, agrupación, recuperación tras interrupciones y consultas posteriores. En vídeo aporta la misma idea desde otra dirección, elevando el análisis desde fotogramas aislados hasta decisiones sobre la línea temporal.
Al tratarse de una beta, la mejor estrategia es experimentar con datos reales en dispositivos compatibles, medir el tamaño y el tiempo de construcción del índice y proteger el resto de la arquitectura mediante una abstracción propia. Media Intelligence apunta a reducir una cantidad considerable de infraestructura personalizada, pero sus resultados siguen necesitando una interfaz depurada, mecanismos de corrección y un diseño cuidadoso de privacidad.