wandres.dev
SWIFT MULTIPLATAFORMA · Linux, Windows, Wasm

Swift en Windows: el estado real del soporte

Qué significa exactamente que Windows sea una plataforma oficial, cómo se instala el toolchain sobre la cadena de MSVC, qué se puede construir hoy con garantías —bibliotecas, herramientas, componentes— y cuáles son las asperezas de rutas, exportación de símbolos y distribución del runtime que nadie cuenta de antemano.

⏱ 19 min

Windows es la plataforma sobre la que más se especula y menos se comprueba. Swift lleva años siendo compatible con ella de forma oficial: hay compilador, gestor de paquetes, depurador, bibliotecas núcleo y compilaciones publicadas para arquitecturas de sesenta y cuatro bits, tanto en Intel como en ARM. Lo que no hay —y aquí está el origen de casi toda la confusión— es un ecosistema de aplicaciones equivalente al de Apple ni una experiencia integrada al estilo de Xcode. El soporte oficial garantiza que el lenguaje y sus bibliotecas funcionen; no garantiza que la biblioteca de terceros que te interesa se haya probado allí ni que exista una capa de interfaz gráfica idiomática. Distinguir esas dos cosas es lo que separa una expectativa razonable de una decepción anunciada.

🎯 Al terminar esta lección sabrás
  • Precisar qué cubre y qué no cubre el soporte oficial de Windows en la distribución de Swift.
  • Instalar el toolchain sobre la cadena de herramientas de MSVC y entender por qué depende de ella.
  • Delimitar las categorías de software que hoy se construyen allí con garantías razonables.
  • Anticipar las asperezas de rutas, exportación de símbolos y distribución del runtime antes de encontrarlas.

Qué significa que Windows sea oficial

Swift en Windows no es una capa de emulación ni un subsistema de Linux: es un objetivo nativo que produce ejecutables de Windows y que sigue la interfaz binaria de MSVC. Esa decisión de diseño es la clave de todo lo demás. Al adoptar la ABI de Microsoft en lugar de traer la suya, Swift se vuelve interoperable con el resto del mundo de Windows —bibliotecas en C y C++ compiladas con el compilador de Microsoft, la API de Win32, la interfaz de componentes del sistema— a cambio de depender del entorno de compilación de Visual Studio.

Lo que se distribuye oficialmente es el compilador, el gestor de paquetes, swift-format, LLDB, la biblioteca estándar, Foundation con su núcleo común, Dispatch, XCTest y el marco de pruebas moderno. Lo que no se distribuye es un entorno de desarrollo integrado: el camino habitual es Visual Studio Code con la extensión oficial, que se apoya en el mismo servidor de lenguaje que usa Xcode.

ℹ️
Qué no cubre el soporte oficial

La garantía es sobre el toolchain y las bibliotecas núcleo, no sobre el ecosistema. Un paquete de terceros funciona en Windows si su autor lo construye y lo prueba allí, y muchos no lo hacen. Antes de comprometerte con una dependencia, mira su matriz de integración continua: es la única evidencia que vale.

Instalar y compilar

El toolchain necesita las herramientas de construcción de Visual Studio con la carga de trabajo de desarrollo en C y C++ y el SDK de Windows, porque de ahí salen el enlazador, la biblioteca de tiempo de ejecución de C y las cabeceras del sistema que Swift consume a través de mapas de módulo. Instalado eso, el resto es una sola orden.

winget install --id Microsoft.VisualStudio.2022.BuildTools -e
winget install --id Swift.Toolchain -e
swift --version

A partir de ahí, el flujo es el mismo que en cualquier otra plataforma: swift build, swift test, swift run. La diferencia notable aparece cuando quieres hablar con el sistema, porque la API de Win32 se expone como un módulo importable con nombres de Swift.

#if os(Windows)
import WinSDK

func rutaDelEjecutable() -> String {
    var buffer = [WCHAR](repeating: 0, count: Int(MAX_PATH))
    let n = GetModuleFileNameW(nil, &buffer, DWORD(buffer.count))
    return String(decodingCString: Array(buffer.prefix(Int(n))), as: UTF16.self)
}
#endif

El entorno de trabajo habitual es Visual Studio Code con la extensión oficial, que aporta el mismo servidor de lenguaje que usa Xcode —completado, saltos a definición, diagnósticos en vivo— y la integración con LLDB para depurar. No hay ni se espera un editor integrado propio, y en la práctica el flujo se parece bastante al de un proyecto de Swift en Linux.

Dos detalles de ese fragmento resumen la experiencia entera. El primero es que la API de Windows habla UTF-16, así que toda frontera con el sistema implica una conversión desde el UTF-8 de String. El segundo es que las funciones vienen con su nombre original y sus tipos originales, sin ninguna capa de amabilidad: importar WinSDK es programar Win32 con la sintaxis de Swift, no una biblioteca de Swift para Windows.

Qué se construye hoy con garantías

Conviene ordenar las posibilidades por grado de riesgo, porque la respuesta honesta no es una sino cuatro.

Lo más sólido son las bibliotecas de dominio: código de modelo, análisis, cálculo, criptografía o protocolo que solo depende de la biblioteca estándar y del núcleo de Foundation. Ahí Windows es tan fiable como Linux, y es también el caso de uso que más valor produce, porque permite compartir la lógica de un producto entre un cliente de Apple y uno de Windows sin duplicarla.

Después vienen las herramientas de línea de órdenes, que funcionan bien salvo por los detalles de consola y de rutas que se detallan más abajo.

En tercer lugar, los componentes empotrados en aplicaciones ajenas: compilar la lógica como biblioteca dinámica y consumirla desde una interfaz escrita en C++ o en C#. Es el patrón más usado en producción real y el que mejor reparte las responsabilidades, porque deja la interfaz en manos del ecosistema que la tiene resuelta.

El caso del servidor merece un matiz, porque suele darse por hecho y no debería. La pila de red asíncrona de Swift declara soporte de Windows, pero un servicio completo arrastra decenas de dependencias transitivas —controladores de base de datos, clientes de mensajería, envoltorios de bibliotecas nativas— y basta con que una no se construya allí para que el edificio entero se venga abajo. Antes de comprometerte, resuelve el grafo de dependencias y comprueba una por una; es media hora de trabajo que ahorra semanas.

Lo más frágil es la interfaz gráfica nativa en Swift. No hay equivalente de SwiftUI. Existen tres caminos y ninguno es cómodo: llamar directamente a Win32 a través de WinSDK, generar proyecciones de la API de componentes moderna con el generador de enlaces correspondiente, o abandonar la idea y escribir la interfaz en la tecnología nativa dejando a Swift la lógica.

flowchart TB
A[Que quiero construir en Windows] --> B[Logica de dominio o biblioteca]
A --> C[Herramienta de linea de ordenes]
A --> D[Componente para una app existente]
A --> E[Interfaz grafica nativa]
B --> F[Riesgo bajo y valor alto]
C --> G[Riesgo bajo con cuidado en rutas y consola]
D --> H[Patron mas usado en produccion]
E --> I[Sin equivalente de SwiftUI, camino aspero]
style F fill:#a6e3a1,color:#11111b
style H fill:#89b4fa,color:#11111b
style I fill:#f38ba8,color:#11111b

Las asperezas que conviene conocer

Las rutas son la primera fuente de incidencias. El separador es la barra invertida, existen letras de unidad, el sistema de ficheros preserva las mayúsculas pero no las distingue, y hay un límite histórico de doscientos sesenta caracteres que solo se levanta si la aplicación lo declara. La consecuencia práctica es que cualquier manipulación de rutas hecha con concatenación de cadenas se romperá: usa siempre los componentes de URL o de FilePath, nunca la aritmética textual.

// Mal: se rompe en cuanto cambia el separador o hay letra de unidad.
let malo = carpeta + "/" + nombre

// Bien: la construccion conoce las reglas de cada sistema.
let bueno = URL(fileURLWithPath: carpeta).appendingPathComponent(nombre)

La consola es la segunda. La página de códigos por defecto no es UTF-8, así que un programa que imprime acentos puede mostrar basura hasta que se cambia la codificación de la terminal o se declara en el manifiesto de la aplicación. Al mismo grupo pertenecen los finales de línea: un fichero de texto escrito con el convenio de Windows y leído por un analizador que solo contempla el salto de línea de Unix produce cadenas con un carácter sobrante al final, y ese carácter invisible es el origen de una cantidad sorprendente de comparaciones que fallan sin explicación.

La distribución del runtime es la tercera y la que más sorprende. En Windows el runtime de Swift se distribuye como bibliotecas dinámicas que tu instalador debe incluir, porque no existe todavía un enlazado estático completo equivalente al de Linux con Musl. Un ejecutable copiado a una máquina sin toolchain no arranca hasta que las bibliotecas del runtime están junto a él o en la ruta de búsqueda.

La cuarta es la exportación de símbolos al cruzar la frontera de una biblioteca dinámica: Windows no exporta nada por defecto, de modo que construir una DLL de Swift consumible desde fuera exige atención a qué se declara público y cómo se enlaza, un problema que en los sistemas basados en ELF simplemente no existe.

Y la quinta, que no es técnica pero cuesta igual, es la disciplina de integración continua. Windows es la plataforma que menos gente construye a diario, así que es también donde las regresiones se acumulan sin que nadie las vea. Un ejecutor de Windows en la matriz de construcción, con las pruebas corriendo en cada envío, es la diferencia entre mantener el soporte y descubrir tres meses después que se perdió.

🪟

La ABI es de Microsoft

Swift habla el mismo binario que MSVC. Por eso interopera con todo el ecosistema nativo y por eso necesita las herramientas de Visual Studio instaladas.

📦

El runtime viaja contigo

Sin enlazado estático completo, tu instalador debe llevar las bibliotecas del runtime. Planifícalo antes de prometer una fecha de entrega.

🧭

Nunca concatenes rutas

Barra invertida, letras de unidad y límite de longitud convierten la aritmética de cadenas en un fallo garantizado. Usa URL o FilePath.

El soporte de una plataforma es una afirmación sobre quién asume el mantenimiento

Preguntar si Swift funciona en Windows es preguntar mal, y la prueba es que la respuesta correcta —sí, oficialmente, desde hace años— no ayuda a decidir nada. Lo que una plataforma soportada significa en realidad es que alguien se ha comprometido a que el compilador construya, las pruebas pasen y las regresiones se arreglen en ese objetivo; es un compromiso de mantenimiento, no una promesa de paridad de experiencia. Y ese compromiso no se hereda hacia arriba: cada capa de la pila lo asume o no lo asume por su cuenta, de modo que la portabilidad efectiva de tu programa es el mínimo de los compromisos de todas sus dependencias, no el del lenguaje. De ahí que la decisión de llevar un producto a Windows no deba tomarse leyendo la documentación de Swift sino auditando la matriz de construcción de cada paquete del que dependes, y midiendo cuánto de tu código sobreviviría si tuvieras que sustituir los que no la tienen. Hay además una segunda lección, menos evidente y más valiosa: el ecosistema de Windows ya resolvió magníficamente el problema de la interfaz de usuario, y competir con esa solución desde Swift es una batalla cara que casi nunca merece la pena. El movimiento inteligente es el inverso —empujar hacia Windows solo la capa donde Swift aporta algo que las alternativas locales no dan, la lógica de dominio compartida con los clientes de Apple, y dejar que cada plataforma ponga la piel que sabe poner. Ese reparto no es una concesión ni una derrota; es la única arquitectura en la que el coste de la segunda plataforma crece con lo que compartes y no con lo que duplicas.

📝
Lo esencial

Windows es un objetivo nativo oficial con ABI de MSVC, lo que exige tener instaladas las herramientas de Visual Studio. El toolchain trae compilador, SwiftPM, LLDB y bibliotecas núcleo, pero no un entorno integrado ni una capa de interfaz. El terreno seguro son las bibliotecas de dominio, las herramientas de consola y los componentes empotrados. Y las asperezas reales son las rutas, la codificación de consola, la distribución del runtime en bibliotecas dinámicas y la exportación explícita de símbolos.

⚔️ Un componente compartido de verdad
  1. Instala el toolchain y construye una biblioteca de dominio existente sin tocar una línea; anota qué dependencias fallan y por qué.
  2. Escribe una función que devuelva la ruta del ejecutable usando WinSDK y compárala con la versión POSIX equivalente.
  3. Construye una herramienta de consola que imprima texto acentuado y arregla la codificación hasta que se vea bien.
  4. Empaqueta la biblioteca como componente dinámico y consúmela desde un programa mínimo escrito en otra tecnología.
  5. Configura un trabajo de integración continua en un ejecutor de Windows y haz que las pruebas se ejecuten en cada envío.