Especificidades de Swift en desarrollo móvil: qué es, características clave y cómo funciona

Autor: IT Sectr Publicado: 2026-06-25 Tiempo de lectura: 17 min

Swift es el lenguaje de programación de Apple que combina el poder de Objective-C con la seguridad de los lenguajes modernos. A diferencia de muchos otros lenguajes, Swift ofrece construcciones únicas: value types con copia automática, Actores con aislamiento a nivel de compilador, Property Wrappers para reutilizar lógica de propiedades y Programación Orientada a Protocolos como alternativa a la herencia clásica. Según la Documentación de Apple Swift, estas características hacen que el código sea más seguro y eficiente.

Puntos Clave

  • Struct — tipo valor, se copia al asignarse, no soporta herencia. Class — tipo referencia, se pasa por referencia, soporta herencia y deinit.
  • Actor — tipo referencia thread-safe con aislamiento a nivel de compilador. El compilador impide el acceso concurrente al estado mutable desde diferentes tareas.
  • Property Wrappers — @Published, @State, @Binding encapsulan la lógica de propiedades en SwiftUI y Combine, eliminando código repetitivo.
  • Generics permiten escribir funciones y estructuras type-safe sin vincularse a un tipo específico. La Programación Orientada a Protocolos usa extension + protocol en lugar de jerarquía de clases.
  • Enum con valores asociados — una herramienta poderosa para modelar estados. Cada caso puede almacenar un conjunto arbitrario de datos de diferentes tipos.

Struct vs Class en Swift: tipos valor y tipos referencia

En Swift, struct es un tipo valor, mientras que class es un tipo referencia. Esta diferencia fundamental determina el comportamiento durante la asignación, el paso a funciones y la gestión de memoria. Struct se copia completamente en cada asignación, class se pasa por referencia — solo se copia el puntero al objeto en el heap.

Semántica de Valor de Struct

Struct implementa automáticamente la semántica de valor — cada instancia es independiente. La mutación de una propiedad de struct solo es posible a través de var, y los métodos que modifican propiedades deben marcarse como mutating. Struct no soporta herencia pero puede conformarse a protocolos. La biblioteca estándar de Swift usa struct para String, Array, Dictionary, Int, Bool — todos los tipos fundamentales son tipos valor.

swift
struct User {
    let id: Int
    var name: String

    mutating func updateName(_ newName: String) {
        name = newName
    }
}

var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Copia completa — instancias independientes

Semántica de Referencia de Class

Class se almacena en el heap y soporta igualdad por referencia. Varias variables pueden referenciar al mismo objeto, los cambios a través de una referencia son visibles en todas las demás. Class soporta herencia, permite sobrescritura de métodos (override), tiene deinit para limpieza de recursos. En Swift, class se usa con menos frecuencia que struct — Apple recomienda comenzar con struct y cambiar a class solo cuando se necesite herencia o semántica de referencia.

swift
class Car {
    let model: String
    var speed: Int

    init(model: String, speed: Int) {
        self.model = model
        self.speed = speed
    }

    func accelerate(_ amount: Int) {
        speed += amount
    }
}

let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Misma referencia — cambios visibles en todas partes

La elección entre struct y class depende de la semántica de los datos. Para configuraciones, modelos de datos y estados usa struct — la copia es más segura y predecible. Para objetos con identidad (sesión de usuario, conexión a base de datos) y cuando se necesita herencia, usa class.

Actor — seguridad en concurrencia en Swift

Actor es un tipo referencia que protege su estado mutable del acceso concurrente. El compilador de Swift garantiza que las variables de actor solo pueden accederse desde el mismo contexto del actor — cualquier acceso externo causa un error de compilación. Esto previene condiciones de carrera a nivel de lenguaje, sin bloqueos manuales.

Aislamiento de Actor

Todas las propiedades y métodos de actor están aislados por defecto. Llamar a un método de actor desde fuera requiere await — Swift cambia la ejecución al actor y viceversa. Actor sincroniza el acceso secuencialmente: las solicitudes de diferentes tareas se ponen en cola. Esto garantiza un estado consistente dentro del actor. Los métodos no aislados (nonisolated) pueden llamarse sin await pero no pueden acceder a propiedades mutables.

swift
actor BankAccount {
    private var balance: Double

    init(initialBalance: Double) {
        balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) throws {
        guard balance >= amount else {
            throw BankError.insufficientFunds
        }
        balance -= amount
    }

    nonisolated func accountInfo() -> String {
        return "Cuenta Bancaria"
    }
}

let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — atómico, sin condición de carrera

Actor es útil para gestores de estado, cachés y servicios de red donde múltiples tareas leen y escriben datos concurrentemente. A diferencia de la sincronización manual mediante NSLock o DispatchQueue, actor se verifica en tiempo de compilación — es imposible cometer un error. Los errores de aislamiento aparecen en tiempo de compilación, no en tiempo de ejecución.

Property Wrappers: @Published, @State, @Binding

Property Wrapper es una anotación que añade comportamiento a una propiedad sin cambiar su declaración. SwiftUI y Combine proporcionan wrappers integrados para gestionar el estado de las vistas. El compilador genera código que envuelve el acceso a la propiedad, añadiendo lógica de observación, sincronización o transformación.

@Published y @State

@Published se usa en clases que conforman ObservableObject para publicar cambios automáticamente. Los suscriptores de Combine reciben notificaciones en cada actualización. @State es el estado local de una vista en SwiftUI. SwiftUI redibuja automáticamente la vista cuando @State cambia. @Binding crea una conexión bidireccional entre una vista padre e hija sin poseer los datos.

swift
import SwiftUI
import Combine

@main
struct CounterApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @State private var greeting: String = "Hola"
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            CounterDisplay(count: $viewModel.count)
            Text(greeting)
            Button("Incrementar", action: viewModel.increment)
        }
    }
}

struct CounterDisplay: View {
    @Binding var count: Int

    var body: some View {
        Text("Conteo: \(count)")
    }
}

Los Property Wrappers personalizados se crean mediante @propertyWrapper y una estructura con un campo wrappedValue. Esto permite reutilizar lógica: validación, normalización, almacenamiento en UserDefaults. Los Property Wrappers reducen significativamente el código repetitivo y hacen explícita la intención del desarrollador mediante anotación.

swift
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Settings {
    @Clamped(range: 0...100) var volume: Int = 50
}

var s = Settings()
s.volume = 150
// s.volume === 100 — valor automáticamente limitado

Generics y Programación Orientada a Protocolos

Generics es un potente mecanismo de Swift para escribir código flexible sin perder seguridad de tipos. Las funciones y tipos genéricos funcionan con cualquier tipo que cumpla las restricciones. La Programación Orientada a Protocolos (POP) es un paradigma de Swift donde protocol + extension reemplazan la herencia clásica de clases, proporcionando composición de comportamiento.

Restricciones Genéricas y Tipos Asociados

Swift permite restringir parámetros genéricos con protocolos (cláusulas where). Protocolo con tipo asociado es el equivalente genérico para protocolos: el tipo concreto se determina en el momento de la conformidad. Esta es la base para colecciones y algoritmos type-safe. La biblioteca estándar de Swift usa activamente generics: Array, Dictionary, Optional — todos son tipos genéricos.

swift
protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    mutating func set(_ value: Value, for key: Key)
}

struct MemoryCache<K: Hashable, V>: Cacheable {
    private var storage: [K: V] = [:]

    typealias Key = K
    typealias Value = V

    func get(_ key: K) -> V? {
        return storage[key]
    }

    mutating func set(_ value: V, for key: K) {
        storage[key] = value
    }
}

func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
    for (index, item) in array.enumerated() {
        if item == target {
            return index
        }
    }
    return nil
}

let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — un método genérico para todos los tipos Equatable

Extension de Protocolo e Implementaciones por Defecto

La extension de protocolo es un mecanismo clave de POP. En una extension se puede proporcionar una implementación por defecto de los métodos del protocolo. Los tipos que se conforman al protocolo obtienen esta implementación gratuitamente y pueden sobrescribirla si es necesario. Esto permite construir jerarquías de comportamiento sin herencia de clases — una alternativa a la herencia múltiple, que no existe en Swift.

swift
protocol Drivable {
    var speed: Double { get set }
    func drive()
}

extension Drivable {
    func drive() {
        if speed > 0 {
            print("Conduciendo a \(speed) km/h")
        }
    }
}

struct Bicycle: Drivable {
    var speed: Double = 15
}

struct Plane: Drivable {
    var speed: Double = 900

    func drive() {
        print("Volando a \(speed) km/h")
    }
}

let bike = Bicycle()
bike.drive() // "Conduciendo a 15.0 km/h" — por defecto

let plane = Plane()
plane.drive() // "Volando a 900.0 km/h" — sobrescrito

POP con extension permite composición a través de múltiples protocolos — un tipo puede conformarse a varios protocolos, obteniendo sus implementaciones. Esto es más flexible que la herencia única de clases, donde toda la jerarquía está fija. La biblioteca estándar de Swift está construida sobre POP: CustomStringConvertible, Equatable, Hashable, Codable — todos son protocolos con extensiones.

Enum con valores asociados y Extension

Enum en Swift es una construcción significativamente más potente que en otros lenguajes. Cada caso puede tener valores asociados — un conjunto arbitrario de datos de cualquier tipo. Combinado con extension y pattern matching, enum se convierte en la base para modelar estados, resultados de operaciones y máquinas de estados finitos.

Valores Asociados y Pattern Matching

Los valores asociados permiten que cada caso almacene datos únicos. Por ejemplo, el caso loading no contiene datos, success contiene el resultado, failure contiene un error. El pattern matching mediante switch extrae los valores asociados y maneja cada caso. El compilador verifica la exhaustividad (exhaustive switch) — esto elimina estados olvidados.

swift
enum NetworkResult<T> {
    case loading
    case success(T)
    case failure(Error)
}

enum MediaFile {
    case image(url: URL, width: Int, height: Int)
    case video(url: URL, duration: Double)
    case audio(url: URL, bitrate: Int)
}

func handle(_ result: NetworkResult<String>) {
    switch result {
    case .loading:
        print("Cargando...")
    case .success(let data):
        print("Recibido: \(data)")
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}

let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)

if case .video(let url, let duration) = file {
    print("Video \(url) duración \(duration)s")
}

Extension para Enum

Swift permite añadir métodos, propiedades computadas y conformidad a protocolos mediante extension para enum. Esto no requiere cambiar la declaración original — el código se mantiene limpio y modular. Los métodos en extension pueden trabajar con valores asociados, calcular datos derivados e implementar lógica de negocio.

swift
extension MediaFile {
    var fileName: String {
        switch self {
        case .image(let url, _, _),
             .video(let url, _),
             .audio(let url, _):
            return url.lastPathComponent
        }
    }

    var description: String {
        switch self {
        case .image(_, let w, let h):
            return "Imagen \(w)x\(h)"
        case .video(_, let d):
            return "Video \(d)s"
        case .audio(_, let b):
            return "Audio \(b) kbps"
        }
    }
}

extension MediaFile: Equatable {
    static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
        return lhs.fileName == rhs.fileName
    }
}

let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Video 120.0s"

Enum con valores asociados + extension es el reemplazo de las clases selladas de Kotlin o las uniones discriminadas de otros lenguajes. Swift usa esta combinación para Result (un tipo estándar en la biblioteca estándar), Optional (Optional es un enum con .none y .some) y el manejo de estados en SwiftUI. El pattern matching garantiza que todos los estados se manejan — los errores en tiempo de ejecución son imposibles.

Preguntas Frecuentes

¿En qué se diferencia struct de class en Swift?

Struct — tipo valor, se copia al asignarse, no soporta herencia. Class — tipo referencia, se pasa por referencia, soporta herencia, deinit y verificación de identidad (===). Apple recomienda comenzar con struct y cambiar a class solo cuando se necesite semántica de referencia.

¿Qué es Actor en Swift y cuándo usarlo?

Actor — tipo referencia thread-safe. El compilador impide el acceso concurrente al estado mutable desde diferentes contextos. Usa Actor para gestores de estado, cachés y servicios a los que acceden múltiples tareas. Llamar a métodos de actor requiere await.

¿Qué Property Wrappers se usan en SwiftUI?

Principales: @State — estado local de la vista; @Binding — conexión con datos sin posesión; @Published — publicación de cambios en ObservableObject; @StateObject — posesión de ObservableObject; @EnvironmentObject — dependencia del entorno. Los wrappers personalizados se crean mediante @propertyWrapper.

¿Qué es la Programación Orientada a Protocolos?

POP — un paradigma donde protocol + extension definen el comportamiento en lugar de la herencia de clases. Un tipo se conforma a múltiples protocolos, obteniendo sus implementaciones gratuitamente. Es composición sobre herencia — más flexible y seguro. La biblioteca estándar de Swift (Equatable, Codable) está construida sobre POP.

¿En qué se diferencia enum en Swift de enum en otros lenguajes?

Enum en Swift soporta valores asociados — cada caso puede almacenar datos arbitrarios de diferentes tipos. Combinado con pattern matching (switch), proporciona un modelo potente para estados, errores y Domain-Driven Design. Optional y Result son enum estándar de Swift con valores asociados.

Resumen

  • Struct vs Class — diferencia fundamental: los tipos valor se copian, los tipos referencia se pasan por referencia. Struct no soporta herencia, class sí. Apple recomienda struct por defecto.
  • Actor — seguridad en concurrencia a nivel de compilador. Estado mutable aislado, acceso mediante await, protección contra condiciones de carrera sin bloqueos manuales.
  • Property Wrappers — @Published, @State, @Binding y personalizados mediante @propertyWrapper. Reducen código repetitivo y hacen explícita la intención mediante anotación.
  • Generics — funciones y tipos type-safe con restricciones where. Protocol + tipo asociado para abstracciones. La biblioteca estándar de Swift está completamente construida sobre generics.
  • Programación Orientada a Protocolos — composición de comportamiento mediante extension de protocolo. Las implementaciones por defecto permiten reutilizar código sin herencia de clases.
  • Enum con valores asociados — un modelo potente para estados. Cada caso almacena datos únicos. El pattern matching garantiza que todos los estados se manejan.
  • Extension — añade métodos, propiedades computadas y conformidad a protocolos para cualquier tipo, incluidos enum. El código se mantiene modular y limpio sin cambiar la declaración original.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto