Programación en Swift y SwiftUI para iOS Developers

Cómo crear una web con Swift

Si eres un iOS developer, es probable que vivas enamorado de la sintaxis de Swift, la seguridad de tipos y el ecosistema de Xcode. Sin embargo, cuando llega el momento de crear un sitio web o un backend para tus apps, a menudo te ves forzado a cambiar de contexto: aprender Node.js, luchar con Python o configurar PHP.

Pero, ¿y si te dijera que no tienes que salir de tu lenguaje favorito? ¿Y si pudieras escribir tu backend y tu sitio web con la misma seguridad y potencia que usas para tus Vistas en SwiftUI?

Bienvenido al mundo de Vapor, el framework web más popular para la programación Swift. En este tutorial aprenderás a construir un sitio web dinámico desde cero, aprovechando tus conocimientos existentes para convertirte en un desarrollador Full Stack real.


¿Qué es Vapor y por qué debería importarte?

Vapor no es un generador de sitios estáticos. Es un framework web HTTP completo, asíncrono y de alto rendimiento, construido sobre SwiftNIO (la librería de red de Apple).

Para un iOS developer, las ventajas son abrumadoras:

  1. Un solo lenguaje: Frontend (iOS) y Backend (Web) en Swift.
  2. Código Compartido: Puedes compartir tus modelos de datos (structs) entre la App y la Web. Si actualizas un modelo, el compilador te avisa en ambos lados.
  3. Xcode: Usas el mejor IDE del mundo con depuración, autocompletado y refactorización para tu web.
  4. Performance: Swift en el servidor es increíblemente rápido y eficiente en memoria comparado con lenguajes interpretados.

El diagrama anterior muestra cómo Vapor maneja las peticiones: de forma no bloqueante, lo que permite manejar miles de conexiones simultáneas con pocos recursos, ideal para la nube.


Paso 1: Configuración del Entorno de Desarrollo

A diferencia de SwiftUI donde solo necesitas Xcode, para el desarrollo web necesitamos instalar algunas herramientas de línea de comandos.

Requisitos

  • macOS (Versión reciente).
  • Xcode 14+ instalado.
  • Homebrew (Gestor de paquetes para macOS).

Instalando Vapor Toolbox

Abre tu terminal. Vamos a instalar la caja de herramientas de Vapor, que nos ayudará a crear y gestionar proyectos.

brew install vapor

Una vez instalado, verifica que todo esté correcto con:

vapor --help

Paso 2: Creando tu Primer Proyecto Web en Swift

Olvídate de File > New Project en Xcode por un segundo. La mejor manera de iniciar un proyecto Vapor es desde la terminal para asegurar que las dependencias se descarguen correctamente.

Navega a tu carpeta de proyectos y ejecuta:

vapor new MiSitioWebSwift -n

El flag -n le dice a Vapor que responda “no” a las preguntas de configuración (usaremos la configuración por defecto). Una vez creado, entra en la carpeta:

cd MiSitioWebSwift
open Package.swift

Al abrir Package.swiftXcode se lanzará y comenzará a descargar las dependencias. Esto puede tardar unos minutos la primera vez.

La Anatomía del Proyecto

Para un iOS developer, la estructura puede parecer extraña al principio, pero tiene sus equivalencias:

  • Package.swift: Es como tu Podfile o la configuración del proyecto. Define las dependencias.
  • Sources/App/configure.swift: Piensa en esto como tu AppDelegate o App.swift. Aquí configuras la base de datos, las rutas y los servicios al iniciar la app.
  • Sources/App/routes.swift: Aquí defines las URLs de tu sitio web (ej. /home/contacto).
  • Public: Aquí van los archivos estáticos (imágenes, CSS, JS).

Paso 3: Hola Mundo en la Web

Vamos a hacer que nuestro servidor funcione. En Xcode, asegúrate de seleccionar el esquema “Run” y como destino “My Mac”. Presiona Cmd + R.

Verás en la consola de Xcode: [ NOTICE ] Server starting on http://127.0.0.1:8080

Abre tu navegador y ve a esa URL. Deberías ver “It works!”.

Creando tu primera Ruta

Abre routes.swift. Vamos a añadir un endpoint que devuelva un JSON, algo típico para una API que consumiría tu app iOS.

func routes(_ app: Application) throws {
    app.get { req in
        return "¡Hola mundo desde Swift!"
    }

    app.get("ios-developer") { req -> String in
        return "El mejor trabajo del mundo."
    }
}

Si guardas y vuelves a ejecutar, al ir a http://127.0.0.1:8080/ios-developer, verás tu mensaje.


Paso 4: Leaf – El Motor de Vistas (El SwiftUI de la Web)

Hasta ahora hemos devuelto texto plano. Pero para crear un sitio web con Swift, necesitamos HTML. Aquí entra Leaf.

Leaf es el lenguaje de plantillas de Vapor. Es similar a HTML pero te permite inyectar código Swift (variables, bucles, condiciones). Es el equivalente a tus Vistas en SwiftUI, pero renderizado en el servidor.

Instalando Leaf

  1. Abre Package.swift y añade Leaf a las dependencias:
.package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),

2. Añádelo al target “App”:

.product(name: "Leaf", package: "leaf"),

3. Abre configure.swift y configura Leaf:

import Leaf // No olvides importar

public func configure(_ app: Application) throws {
    app.views.use(.leaf)
    // ... resto del código
}

Creando tu primera Plantilla

Crea una carpeta llamada Resources en la raíz de tu proyecto, y dentro una carpeta Views. Crea un archivo llamado index.leaf.

Nota: Xcode no resalta la sintaxis de .leaf por defecto, pero es básicamente HTML.

<!DOCTYPE html>
<html>
<head>
    <title>#(titulo)</title> <style>
        body { font-family: -apple-system, sans-serif; padding: 20px; }
        h1 { color: #F05138; } /* Swift Orange */
    </style>
</head>
<body>
    <h1>Hola, #(nombre)</h1>
    <p>Bienvenido al desarrollo web con Swift.</p>
    
    #if(esIosDeveloper):
        <pVeo que usas Xcode! Estás en casa.</p>
    #endif
</body>
</html>

Renderizando la Vista desde Swift

Vuelve a routes.swift. Vamos a inyectar datos en esa plantilla.

app.get("web") { req -> EventLoopFuture<View> in
    let contexto = [
        "titulo": "Mi Web Swift",
        "nombre": "Desarrollador",
        "esIosDeveloper": "true"
    ]
    return req.view.render("index", contexto)
}

Ejecuta y visita /web. ¡Acabas de renderizar HTML dinámico usando Swift!


Paso 5: Modelado de Datos (Codable al Rescate)

Aquí es donde la programación Swift brilla. En lugar de usar diccionarios [String: Any] (que son propensos a errores), usaremos structs que conformen a ContentContent es un envoltorio de Vapor sobre Codable.

Imagina que queremos mostrar una lista de cursos de iOS.

Crea un nuevo archivo Models/Curso.swift:

import Vapor

struct Curso: Content {
    let id: Int
    let nombre: String
    let dificultad: String
}

Ahora, actualicemos routes.swift para pasar un array de cursos a la vista:

app.get("cursos") { req -> EventLoopFuture<View> in
    let cursos = [
        Curso(id: 1, nombre: "SwiftUI Masterclass", dificultad: "Intermedio"),
        Curso(id: 2, nombre: "Vapor desde Cero", dificultad: "Avanzado")
    ]
    
    // Pasamos el struct directamente. Leaf entenderá la estructura.
    return req.view.render("cursos", ["cursos": cursos])
}

Y creamos Resources/Views/cursos.leaf:

<h1>Lista de Cursos</h1>
<ul>
    #for(curso in cursos):
        <li>
            <strong>#(curso.nombre)</strong> - Nivel: #(curso.dificultad)
        </li>
    #endfor
</ul>

¿Notas la similitud con ForEach en SwiftUI? La lógica es idéntica: iterar sobre una colección de datos tipados.


Paso 6: Arquitectura Full Stack (El Santo Grial)

Esta sección es la más importante para un ios developer. La mayor pesadilla en el desarrollo de apps es mantener sincronizada la API con la App. Si el backend cambia el nombre de un campo en el JSON, la app crashea.

Con Swift en el backend, podemos solucionar esto compartiendo código.

Creando un Paquete Compartido

  1. Crea una carpeta nueva fuera de tu proyecto Vapor llamada SharedModels.
  2. Inicia un paquete Swift: swift package init --type library.
  3. Mueve tu struct Curso.swift ahí (asegúrate de que sea public y conforme a Codable).
  4. Importa este paquete local tanto en tu proyecto Vapor (en Package.swift) como en tu proyecto de iOS en Xcode.

Ahora, Curso es la única fuente de la verdad.

  • En Vapor: struct Curso: Content (Vapor añade conformidad a Content automáticamente si es Codable).
  • En iOS: let cursos = try JSONDecoder().decode([Curso].self, from: data).

Si cambias nombre por titulo en el paquete compartido, Xcode te dará un error de compilación tanto en el proyecto del servidor como en la app de iOS. Esto es seguridad de tipos en todo el stack.


Paso 7: Persistencia de Datos (Fluent)

Un sitio web real necesita base de datos. Vapor usa Fluent, un ORM (Object Relational Mapper) que te permite interactuar con bases de datos usando Swift puro, sin escribir SQL. Es como Core Data, pero mucho más amigable y diseñado para servidores.

Para configurar una base de datos SQLite (ideal para pruebas):

  1. Añade Fluent y FluentSQLiteDriver a Package.swift.
  2. En configure.swift:
import Fluent
import FluentSQLiteDriver

public func configure(_ app: Application) throws {
    app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
    
    // Registrar migraciones
    app.migrations.add(CreateCurso())
    try app.autoMigrate().wait()
}

Definiendo el Modelo de Base de Datos

final class CursoModel: Model, Content {
    static let schema = "cursos"
    
    @ID(key: .id)
    var id: UUID?

    @Field(key: "nombre")
    var nombre: String

    init() { }

    init(id: UUID? = nil, nombre: String) {
        self.id = id
        self.nombre = nombre
    }
}

Ahora puedes guardar y recuperar datos usando futuros asíncronos:

// Guardar un curso
app.post("api", "curso") { req -> EventLoopFuture<CursoModel> in
    let curso = try req.content.decode(CursoModel.self)
    return curso.save(on: req.db).map { curso }
}

// Obtener todos los cursos
app.get("api", "cursos") { req in
    return CursoModel.query(on: req.db).all()
}

Paso 8: Estilizando tu Sitio (CSS y JavaScript)

Aunque el backend es Swift, el navegador sigue hablando HTML/CSS. Vapor sirve archivos estáticos desde la carpeta Public.

  1. Crea Public/styles.css.
  2. En configure.swift, asegúrate de tener: app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)).
  3. En tu index.leaf, vincula el CSS: <link rel="stylesheet" href="/styles.css">.

Como desarrollador acostumbrado a SwiftUI, CSS puede parecer tedioso. Te recomiendo usar un framework CSS como Tailwind o Bootstrap. Simplemente descarga el CSS minificado, ponlo en la carpeta Public y úsalo en tus clases de HTML en Leaf.


Optimización para Google y SEO

Al crear un sitio web con Swift usando Vapor, tienes una ventaja enorme en SEO (Search Engine Optimization) comparado con las Single Page Applications (SPA) hechas en React o Angular.

Vapor renderiza el HTML en el servidor (Server Side Rendering – SSR). Esto significa que cuando el bot de Google visita tu página, recibe el contenido completo de inmediato, sin tener que ejecutar JavaScript.

Para optimizar aún más:

  1. Metadatos Dinámicos: En tus rutas, pasa variables para el título y la descripción a Leaf.
<meta name="description" content="#(descripcion)">

2. Velocidad: Swift es compilado. El tiempo de respuesta de tu servidor será de milisegundos, un factor clave para el ranking en Google (Core Web Vitals).


    Despliegue: Llevando tu Swift a la Nube

    Tu sitio funciona en localhost. ¿Cómo lo ve el mundo?

    Swift se ejecuta perfectamente en Linux. La forma estándar de desplegar Vapor es usando Docker.

    1. El proyecto de Vapor ya trae un Dockerfile generado.
    2. Puedes usar servicios como Fly.ioRender o Heroku.
    3. Con Fly.io, por ejemplo, solo necesitas instalar su CLI y ejecutar fly launch. Detectará el Dockerfile y subirá tu sitio Swift a la nube en minutos.

    Conclusión: El Futuro es Swift en Todas Partes

    Hemos recorrido un largo camino. Has configurado un servidor, creado rutas, renderizado HTML con Leaf, conectado una base de datos con Fluent y entendido el poder de compartir código.

    Como iOS developer, aprender Vapor no solo te permite crear un sitio web con Swift; te transforma en un desarrollador más completo. Entenderás mejor cómo funcionan las APIs que consumes en tus apps, podrás prototipar backends rápidamente y tendrás el control total de tu producto, desde el píxel en la pantalla del iPhone hasta el byte en la base de datos del servidor.

    El ecosistema de la programación Swift está madurando. Ya no es solo para dispositivos Apple. Es para la web, es para el servidor, es para el mundo.

    ¿Tu próximo paso? Intenta crear un formulario web en Vapor que guarde datos, y luego crea una App en SwiftUI que lea esos mismos datos. La sensación de ver tu código Swift fluyendo de extremo a extremo es incomparable.

    Si tienes cualquier duda sobre este artículo, contacta conmigo y estaré encantado de ayudarte 🙂. Puedes contactar conmigo en mi perfil de X o en mi perfil de Instagram

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Previous Article

    .animation() vs withAnimation() in SwiftUI

    Next Article

    .Animation vs .Transition en SwiftUI

    Related Posts