En la web abierta, un enlace <a download> o un endpoint de PDF/CSV guarda un archivo en el dispositivo. Dentro de un WebView, ese mismo toque es un no-op silencioso. Android solo expone las descargas mediante un DownloadListener del host, e iOS WKWebView no trae gestor de descargas. Un runtime propio del host hace la ponte de la descarga: una API que guarda un archivo real en cada motor.
Android: las descargas necesitan un listener del host
La documentación de Android y la comunidad coinciden: un WebView estándar no descarga nada por sí solo. Cuando el usuario toca un enlace de descarga, el motor entrega los parámetros —url, userAgent, contentDisposition, mimeType, contentLength— al DownloadListener que registras con setDownloadListener, o no pasa nada. Sin ese listener, un botón de "Descargar factura PDF" que funciona en Chrome no hace absolutamente nada dentro del WebView de la app. Conectarlo implica poseer la instancia, parsear el nombre de archivo desde Content-Disposition, solicitar WRITE_EXTERNAL_STORAGE (o delegar a DownloadManager) y mostrar progreso. Las descargas blob y <a download> son lo mismo: la página no puede escribir el archivo; solo el host puede.
iOS: WKWebView no tiene gestor de descargas
El WebView de Apple no tiene una canalización de descarga integrada. Los análisis de las limitaciones de WKWebView lo dicen claro: "No hay gestor de descargas, así que las descargas fallan o no hacen nada en silencio". Antes de iOS 15 la única vía era interceptar la petición y reimplementar el guardado. iOS 15 añadió WKDownloadDelegate, pero el host debe implementarlo: el atributo <a download> solo señala intención, y la ejecución vive en la app. El comportamiento por defecto es peor que un no-op: el archivo a menudo se abre dentro del marco web y rompe el historial de navegación, o el toque se traga sin feedback.
Qué se rompe cuando la descarga muere
- Facturas y estados nunca llegan. Un botón "Descargar PDF" que funciona en Safari no hace nada en el WebView.
- Exportaciones CSV e informes desaparecen. Los paneles SaaS y financieros que exportan datos pierden la única salida.
- Actualizaciones y APKs se estancan. Los flujos de actualización que empujan un APK o un paquete fallan dentro del navegador integrado.
- Los navegadores integrados empeoran. WeChat, Douyin y similares rara vez exponen una superficie de descarga, así que el fallo se oculta en el QA de escritorio.
La solución del runtime
Un runtime propio del host hace la ponte de la descarga. La página llama a una API uniforme —runtime.download(url, options) o runtime.saveFile(blob)— y el host guarda con DownloadManager en Android y WKDownloadDelegate en iOS. Nombre, destino, progreso y permiso de almacenamiento se manejan de forma nativa y consistente. Una integración, cada motor, un archivo guardado.
| Dimensión | WebView | Runtime propio del host |
|---|---|---|
| Disparador | <a download> es no-op | runtime.download() uniforme |
| Ruta Android | El host debe cablear DownloadListener | DownloadManager nativo |
| Ruta iOS | Sin gestor de descargas | WKDownloadDelegate nativo |
| Guardar blob | No desde la página | Una API |
| Permisos | Manual por app | Ponte nativa |
| Consistencia | Por motor | Uniforme |
Una app nativa guarda un archivo en cada dispositivo. Un WebView estándar apenas en ninguno. Un runtime da a la página una acción de guardar — y un archivo real al final.
Fuentes
- dev.to — Cómo funciona el listener de descargas de WebView en Android — sin
setDownloadListener, tocar un enlace de descarga no hace nada; el listener entregaurl/mimeType/filenameal host. - DevGex — Habilitar la descarga de archivos en Android WebView —
setDownloadListenermásDownloadManager; se requieren permisos de almacenamiento. - gotoapp.store — Limitaciones de WKWebView — "No hay gestor de descargas, así que las descargas fallan o no hacen nada en silencio".
- Medium — Descargar archivos desde un WKWebView desde iOS 15 —
WKDownloadDelegate;<a download>señala intención pero la ejecución aún necesita el host;canShowMimeType. - GitHub MAUI — WebView en iOS no puede descargar archivos — el WebView iOS por defecto abre el archivo en página o rompe la navegación; hay que añadir manejadores
WKDownloadDelegate.