Evaluación Final – Función de Control Parental para VLC-iOS

Cuando comencé este proyecto, el objetivo era claro: implementar un sistema de Control Parental en VLC para iOS que pudiera proteger funciones sensibles, como eliminar archivos multimedia, acceder a configuraciones y abrir transmisiones de red, requiriendo autenticación a través de un código de acceso o Face ID. El objetivo era proporcionar una experiencia más segura para las familias, particularmente para padres que comparten sus dispositivos con niños pequeños.

Pero desde el principio, me di cuenta de que esta tarea no sería simple. De hecho, resultó ser la experiencia técnica más desafiante que he tenido, mucho más compleja que cualquier cosa que haya enfrentado en la universidad, proyectos personales o trabajo freelance. Y no solo por el código. Este también fue mi primer proyecto colaborativo de código abierto, lo que significaba aprender a navegar por una base de código compartida, respetar los cambios de otros contribuyentes y mantener un historial de Git limpio y revisable, algo que nunca había hecho antes.

Primeros Pasos y Desafíos de Git

A principios de junio, comencé diseñando la estructura de una nueva clase, ParentalControlCoordinator, que manejaría todos los aspectos de la autenticación. Sin embargo, rápidamente tuve problemas al trabajar con Git. Después de ejecutar un git rebase master, accidentalmente introduje docenas de commits no relacionados en mi merge request. Me tomó más de una semana entender cómo limpiar el historial de commits correctamente usando git reset y git push --force, pero eventualmente pude recuperar la rama y dejarla lista para desarrollo nuevamente.

Primera Versión – Lógica Básica de Autenticación

Una vez que los problemas de Git fueron resueltos, comencé a experimentar con la lógica real de la funcionalidad. El primer método que creé fue:

func authenticate(completion: @escaping (Bool) -> Void)
Pantalla de Configuración con Toggles
Función Swift Authenticate

Verificaba si el control parental estaba habilitado y, si es así, llamaba:

keychain.validateSecret(allowBiometricAuthentication: true, isCancellable: true)
Pantalla de Entrada de Código

Esto funcionaba en algunos escenarios pero reveló una gran falla arquitectónica: la funcionalidad de control parental estaba vinculada al Passcode Lock, una funcionalidad que solicita al usuario que ingrese un código de acceso al abrir la aplicación. Si el Passcode Lock estuviera deshabilitado, el control parental no funcionaría en absoluto.

Independencia Entre Funcionalidades

Para resolver esto, creé una entrada separada en el keychain dedicada al control parental. Luego refactoricé el ParentalControlCoordinator para eliminar todas las dependencias de UserDefaults y reutilicé el KeychainCoordinator solo para lógica relacionada con mostrar la pantalla de código de acceso. Esto permitió que el control parental y el bloqueo por código funcionaran completamente independientes, como deberían.

Implementación del Keychain

Protegiendo Acciones – Método por Método

Con la lógica de autenticación funcionando, pasé a proteger funcionalidades individuales. Para evitar duplicación de código, creé un método centralizado:

func authorizeIfParentalControlIsEnabled(action: @escaping @convention(block) () -> Void, fail: (@convention(block) () -> Void)? = nil)
Función Swift Authorize

Este método era entonces llamado antes de cada acción restringida. Aquí está cómo fue integrado en toda la aplicación:

  • Renombrar un archivo: Dentro de EditActions.rename(...)
  • Eliminar un archivo: En EditActions.delete(...)
  • Abrir la pantalla de configuraciones: Via TabBarCoordinator usando tabBarController(_:shouldSelect:) y handleAppDidBecomeActive()
  • Abrir una transmisión de red: En _openURLStringAndDismiss()
  • Descargar archivos: También en _openURLStringAndDismiss()
  • Acceder a archivos locales: En RemoteNetworkDataSource, via tableView(_:didSelectRowAt:)
  • Agregar/eliminar archivos de playlists/grupos de medios: En EditActions, envolviendo la lógica con authorizeIfParentalControlIsEnabled

Lógica de Sesión y Timeout

Para evitar abrumar al usuario con solicitudes constantes de código de acceso, implementé una sesión de desbloqueo temporal. Después de una autenticación exitosa, un timestamp era almacenado usando:

private var lastAuthenticationDate: Date?

Luego verifiqué el tiempo transcurrido usando:

var isParentalControlUnlocked: Bool

Esto permitía a los usuarios realizar múltiples acciones protegidas dentro de una ventana de 30 segundos sin re-autenticar.

Autenticación Biométrica – El Último Obstáculo

El último y más frustrante desafío fue la integración del Face ID. Si el usuario abría la aplicación usando Face ID (via Passcode Lock), luego intentaba autenticar nuevamente para control parental, la pantalla del Face ID aparecería repetidamente y nunca se descartaría.

Aunque la autenticación fuera técnicamente exitosa, el prompt biométrico permanecería visible. Intenté de todo, desde refactorizar los métodos existentes hasta crear un nuevo showPasscodeController() dentro del ParentalControlCoordinator, y verificar LAContext, pero el problema persistía.

Pantalla de Autenticación Face ID

Estado Actual y Lo Que Queda

La funcionalidad está actualmente en un estado avanzado y testeable. Protege exitosamente todas las acciones principales y proporciona una buena experiencia de usuario gracias a la lógica de sesión de desbloqueo.

Sin embargo, la merge request aún no ha sido fusionada upstream debido al conflicto no resuelto con Face ID, que parece requerir investigación más profunda y colaboración.

Reflexiones

Este proyecto ha sido un viaje increíble. Tuve la libertad de diseñar la lógica y estructurar la arquitectura. Esa autonomía creativa es uno de los aspectos más gratificantes de trabajar en código abierto.

Al mismo tiempo, me llevó a mis límites. Tuve que aprender Git de la manera difícil, sumergirme en una base de código grande y arreglar bugs que ni siquiera sabía cómo describir al principio.

Aunque aún no ha sido fusionado, creo que este es el primer borrador sólido de una funcionalidad útil. Una que puede ser refinada y eventualmente agregada al VLC para iOS para mejorar la experiencia para familias alrededor del mundo.