Programación en Swift y SwiftUI para iOS Developers

Mejores paquetes para aplicaciones en SwiftUI

El ecosistema de desarrollo de Apple ha experimentado una transformación radical en los últimos años. Con la llegada de SwiftUI, la forma en que construimos interfaces de usuario pasó de ser imperativa (UIKit/AppKit) a declarativa. Sin embargo, ningún desarrollador es una isla, y tratar de reinventar la rueda para cada funcionalidad de tu aplicación es el camino más rápido hacia el agotamiento.

Aquí es donde entra el poder del Swift Package Manager (SPM) y la comunidad de código abierto.

En este artículo tutorial, exploraremos los 10 paquetes esenciales que todo desarrollador de SwiftUI debería tener en su arsenal. No solo los listaremos; analizaremos por qué son necesarios, cómo resuelven problemas específicos de SwiftUI y veremos ejemplos de código para que puedas integrarlos en tu proyecto de Xcode hoy mismo.


¿Por qué usar Paquetes de Terceros en SwiftUI?

Antes de sumergirnos en la lista, es vital entender la filosofía. Apple nos da herramientas increíbles (URLSessionCoreDataMapKit), pero a menudo estas API nativas requieren mucho código repetitivo (“boilerplate”) o carecen de funcionalidades específicas de UI que los usuarios modernos esperan.

Los paquetes seleccionados a continuación cumplen tres criterios:

  1. Swift-First: Escritos en Swift, aprovechando características modernas como Concurrencia (async/await) y Genéricos.
  2. Mantenimiento Activo: Bibliotecas vivas que se actualizan con cada versión de iOS.
  3. Compatibilidad con SwiftUI: No son simples wrappers de UIKit, sino que están diseñados para el ciclo de vida de las vistas de SwiftUI.

1. Kingfisher (Carga y Caché de Imágenes)

Aunque SwiftUI introdujo AsyncImage en iOS 15, sigue siendo básico. Carece de un sistema de caché avanzado en disco, transiciones complejas y manejo robusto de errores. Kingfisher es el estándar de oro para manejar imágenes desde la red.

¿Por qué usarlo?

Si tu app muestra un feed de noticias, perfiles de usuarios o una galería, necesitas caché. Sin él, tu app descargará la misma imagen una y otra vez, consumiendo los datos del usuario y ralentizando la UI.

Implementación

Kingfisher ofrece una vista KFImage que actúa como un sustituto directo de la vista Image nativa.

import SwiftUI
import Kingfisher

struct UserProfileView: View {
    let imageURL: URL

    var body: some View {
        KFImage(imageURL)
            .placeholder {
                // Muestra esto mientras carga
                ProgressView()
            }
            .onFailure { error in
                print("Error de carga: \(error)")
            }
            .resizable()
            .fade(duration: 0.25) // Transición suave
            .aspectRatio(contentMode: .fill)
            .frame(width: 150, height: 150)
            .clipShape(Circle())
            .cacheMemoryOnly() // Opcional: Configuración de caché
    }
}

2. The Composable Architecture (TCA)

A medida que tu aplicación crece, el manejo del estado (@State@ObservedObject) puede volverse caótico. TCA, creado por Point-Free, es más que una librería; es un paradigma.

¿Por qué usarlo?

Proporciona una forma consistente de manejar el estado, la composición de funcionalidades, los efectos secundarios (como llamadas a API) y, lo más importante, hace que tu lógica sea 100% testeable.

Implementación Básica

TCA requiere cambiar tu mentalidad, pero el resultado es un código robusto.

import ComposableArchitecture
import SwiftUI

// 1. Definir el Estado y las Acciones
struct CounterFeature: Reducer {
    struct State: Equatable {
        var count = 0
    }
    
    enum Action {
        case decrementButtonTapped
        case incrementButtonTapped
    }
    
    // 2. Definir la lógica (Reducer)
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .decrementButtonTapped:
                state.count -= 1
                return .none
            case .incrementButtonTapped:
                state.count += 1
                return .none
            }
        }
    }
}

// 3. La Vista
struct CounterView: View {
    let store: StoreOf<CounterFeature>
    
    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            HStack {
                Button("-") { viewStore.send(.decrementButtonTapped) }
                Text("\(viewStore.count)")
                Button("+") { viewStore.send(.incrementButtonTapped) }
            }
        }
    }
}

3. Lottie (Animaciones Vectoriales)

Las animaciones dan vida a una app, pero crear animaciones complejas en código es difícil. Lottie (de Airbnb) permite renderizar animaciones creadas en Adobe After Effects y exportadas como JSON.

¿Por qué usarlo?

Permite animaciones de alta calidad, escalables y pequeñas en tamaño de archivo. Es perfecto para pantallas de “Éxito”, loaders personalizados o tutoriales de onboarding.

Implementación

Usaremos la librería lottie-ios que ahora tiene soporte oficial para SwiftUI.

import SwiftUI
import Lottie

struct SuccessView: View {
    var body: some View {
        VStack {
            LottieView(animation: .named("success_confetti"))
                .playing(loopMode: .playOnce)
                .resizable()
                .frame(width: 200, height: 200)
            
            Text("¡Operación completada!")
                .font(.title)
        }
    }
}

4. SwiftUI-Introspect

A veces, SwiftUI es demasiado abstracto. Puede que necesites acceder al UIScrollView subyacente de una List para quitar el rebote, o al UITextField para forzar el foco de una manera específica.

¿Por qué usarlo?

SwiftUI-Introspect te permite acceder a los componentes de UIKit (iOS) o AppKit (macOS) que están “debajo del capó” de las vistas de SwiftUI, sin romper la jerarquía declarativa.

Implementación

Un caso de uso común: deshabilitar el scroll en una lista específica.

import SwiftUI
import SwiftUIIntrospect

struct NoScrollListView: View {
    var body: some View {
        ScrollView {
            Text("Contenido")
        }
        .introspect(.scrollView, on: .iOS(.v15, .v16, .v17)) { scrollView in
            // Acceso directo al componente UIKit
            scrollView.isScrollEnabled = false
            scrollView.backgroundColor = .red
        }
    }
}

5. Pulse (Logger de Red y Depuración)

Depurar llamadas de red URLSession puede ser frustrante si solo usas print()Pulse es un logger de red potente que te permite ver las peticiones HTTP, cabeceras y respuestas JSON directamente en tu dispositivo iOS, sin necesidad de conectar el debugger de Xcode.

¿Por qué usarlo?

Es vital para equipos de QA y desarrolladores. Puedes agitar el dispositivo para ver el historial de red, compartir logs como archivos y ver imágenes decodificadas.

Implementación

La integración es sencilla si inyectas el URLSessionProxyDelegate.

import SwiftUI
import Pulse
import PulseUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onShake {
                    // Muestra la consola de Pulse al agitar
                    presentPulse() 
                }
        }
    }
}

// Vista para mostrar la consola (normalmente en una hoja debug)
struct DebugView: View {
    var body: some View {
        NavigationView {
            ConsoleView()
        }
    }
}

6. Exyte/PopupView

SwiftUI tiene .alert y .sheet, pero ¿qué pasa con los “Toasts” (mensajes flotantes) o popups personalizados que no bloquean toda la pantalla? PopupView de Exyte es la solución más elegante.

¿Por qué usarlo?

Permite crear notificaciones in-app no intrusivas (estilo Android Toast o notificaciones de iOS) con animaciones muy cuidadas.

Implementación

import SwiftUI
import PopupView

struct ContentView: View {
    @State var showToast = false

    var body: some View {
        VStack {
            Button("Mostrar Notificación") {
                showToast = true
            }
        }
        .popup(isPresented: $showToast) {
            HStack {
                Image(systemName: "checkmark.circle")
                Text("Guardado correctamente")
            }
            .padding()
            .background(Color.black.opacity(0.8))
            .foregroundColor(.white)
            .cornerRadius(12)
        } customize: {
            $0
                .type(.floater(verticalPadding: 20))
                .position(.top)
                .animation(.spring())
                .autohideIn(2)
        }
    }
}

7. Factory (Inyección de Dependencias)

La Inyección de Dependencias (DI) es crucial para crear código desacoplado. Aunque SwiftUI tiene @EnvironmentObject, a veces es demasiado “mágico” o difícil de gestionar fuera de las Vistas. Factory es una librería moderna, segura en hilos y muy “Swifty”.

¿Por qué usarlo?

Reemplaza patrones antiguos como Swinject. Te permite definir tus servicios (API, Base de Datos) en un contenedor y recuperarlos fácilmente en tus ViewModels.

Implementación

import Factory
import SwiftUI

// 1. Definir el contenedor
extension Container {
    var dataService: Factory<DataServiceProtocol> { 
        self { NetworkDataService() }.shared
    }
}

// 2. Usarlo en un ViewModel
class ContentViewModel: ObservableObject {
    // Inyección automática
    @Injected(\.dataService) private var dataService 
    
    func loadData() {
        dataService.fetch()
    }
}

8. MarkdownUI

Muchas aplicaciones necesitan renderizar texto rico que proviene de un backend (términos y condiciones, descripciones de productos, artículos de blog). SwiftUI tiene soporte básico para Markdown, pero MarkdownUI ofrece soporte completo, incluyendo tablas, bloques de código, citas y estilos personalizables.

¿Por qué usarlo?

Si tu CMS envía contenido en Markdown, renderizarlo nativamente en SwiftUI con estilos perfectos es difícil sin esta librería.

Implementación

import SwiftUI
import MarkdownUI

struct ArticleView: View {
    let markdownContent = """
    # Título Principal
    Este es un texto en **negrita** y una lista:
    - Elemento 1
    - Elemento 2
    
    ```swift
    print("Hola Mundo")
    ```
    """

    var body: some View {
        ScrollView {
            Markdown(markdownContent)
                .markdownTheme(.gitHub) // Estilo similar a GitHub
                .padding()
        }
    }
}

9. Swift Algorithms

Este paquete es mantenido por la propia Apple. Swift Algorithms incluye una suite de algoritmos de secuencia y colección que no están en la librería estándar.

¿Por qué usarlo?

A menudo escribimos lógica compleja en nuestros ViewModels para filtrar o agrupar datos para la UI. Este paquete simplifica esas tareas. Funciones como chunkeduniquedwindows o randomSample son salvavidas.

Implementación

Supongamos que quieres mostrar una lista de productos en una cuadrícula de 2 columnas, pero necesitas procesar los datos antes de dárselos a un LazyVGrid.

import Algorithms

let products = ["A", "B", "C", "D", "E"]

// Divide el array en trozos de 2
let chunks = products.chunks(ofCount: 2)

for chunk in chunks {
    print(chunk) // ["A", "B"], ["C", "D"], ["E"]
}

// Eliminar duplicados basado en una propiedad
struct User { let id: Int; let name: String }
let users = [User(id: 1, name: "A"), User(id: 1, name: "B")]
let uniqueUsers = users.uniqued(on: \.id)

10. RevenueCat (Compras In-App)

Implementar StoreKit nativamente es un dolor de cabeza: validación de recibos, manejo de suscripciones, periodos de prueba, errores de servidor. RevenueCat es el estándar de la industria para manejar IAP (In-App Purchases).

¿Por qué usarlo?

Ofrece un SDK que simplifica el proceso de compra a unas pocas líneas de código y un backend que gestiona la complejidad de la validación de recibos y el estado de la suscripción (Free vs Premium) entre plataformas (iOS y Android).

Implementación

import RevenueCat
import SwiftUI

class SubscriptionManager: ObservableObject {
    @Published var isPremium = false
    
    func purchase() {
        Purchases.shared.getOfferings { (offerings, error) in
            if let package = offerings?.current?.monthly {
                Purchases.shared.purchase(package: package) { (transaction, info, error, userCancelled) in
                    if let info = info, info.entitlements["pro_access"]?.isActive == true {
                        self.isPremium = true
                    }
                }
            }
        }
    }
}

Consejos Finales para la Gestión de Paquetes

¿SPM o CocoaPods?

La respuesta es rotunda: Usa Swift Package Manager (SPM). Está integrado en Xcode, es rápido y no requiere modificar tu proyecto con archivos .xcworkspace externos. CocoaPods es tecnología “legacy” para nuevos proyectos.

¿Cómo mantener el rendimiento?

Añadir paquetes aumenta el tiempo de compilación.

  1. Sé selectivo: No instales una librería entera para usar una sola función.
  2. Usa versiones exactas: En tu archivo de configuración de paquetes, trata de bloquear versiones mayores (ej. “Up to Next Major”) para evitar que una actualización rompa tu código, pero permitiendo parches de seguridad.

Conclusión

El desarrollo en SwiftUI se trata de composición. Al utilizar estos paquetes, no estás “haciendo trampa”, estás construyendo sobre los hombros de gigantes para entregar valor a tus usuarios más rápido.

Mi recomendación para tu próximo paso: Elige uno de estos paquetes que resuelva un problema actual en tu app e impleméntalo. Si estás luchando con imágenes, ve por Kingfisher. ¿El código es un lío? Prueba TCA o Factory.

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

Cómo mostrar una imagen desde una URL en SwiftUI

Next Article

Cómo añadir compras integradas en una aplicación en SwiftUI

Related Posts