0Pricing
Swift Academy · Lección

Patrones comunes de wrappers (validación, caché)

Cree wrappers habituales de validation y caching : valide la entrada, proporcione valores predeterminados, memorice operaciones costosas y exponga indicadores auxiliares mediante valores proyectados.

Patrones comunes de wrappers (validación, caché) es una lección gratuita de Swift Academy en CoddyKit. Esta es la lección 3 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Swift Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Swift Academy incluye 3 lecciones en total.

Lo que construiremos

En esta lección se muestran dos patrones prácticos:

  • Validación: acepte o corrija entradas con valores predeterminados y mensajes.
  • Almacenamiento en caché: almacene resultados y exponga $helpers como clear() y isCached.

Validación: NonEmpty

@NonEmpty protege frente a entradas vacías y proporciona un valor predeterminado. El projectedValue indica si el último valor asignado se corrigió automáticamente.

@propertyWrapper
struct NonEmpty {
    private var value: String = ""
    private(set) var lastFixed: Bool = false

    var wrappedValue: String {
        get { value }
        set {
            if newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                value = "N/A"          // default fallback
                lastFixed = true
            } else {
                value = newValue
                lastFixed = false
            }
        }
    }

    var projectedValue: Bool { lastFixed } // $name -> was fixed?

    init(wrappedValue: String) { self.wrappedValue = wrappedValue }
}

struct Profile {
    @NonEmpty var displayName: String = "  "
}
var p = Profile()
print(p.displayName)  // "N/A"
print(p.$displayName) // true (was fixed)

Validación basada en reglas

@Validated es genérico y acepta un closure de regla además de un valor alternativo. Úselo para comprobaciones de longitud, rango o personalizadas.

@propertyWrapper
struct Validated<Value> {
    private var value: Value
    private let rule: (Value) -> Bool
    private let fallback: Value
    private(set) var lastValid: Bool = true

    var wrappedValue: Value {
        get { value }
        set {
            if rule(newValue) {
                value = newValue
                lastValid = true
            } else {
                value = fallback
                lastValid = false
            }
        }
    }

    var projectedValue: Bool { lastValid } // $prop -> is valid?

    init(wrappedValue: Value, rule: @escaping (Value) -> Bool, fallback: Value) {
        self.rule = rule
        self.fallback = fallback
        self.value = wrappedValue
        self.lastValid = rule(wrappedValue)
        if !lastValid { self.value = fallback }
    }
}

struct Form {
    @Validated(rule: { $0.count >= 3 }, fallback: "???")
    var nickname: String = "hi"
}
var f = Form()
print(f.nickname)   // "???" (invalid -> fallback)
print(f.$nickname)  // false

Almacenamiento en caché: memoización diferida

@LazyCached memoiza un valor a partir de un closure. $result expone los helpers isCached y clear().

@propertyWrapper
struct LazyCached<Value> {
    private var storage: Value?
    private let build: () -> Value

    var wrappedValue: Value {
        mutating get {
            if let v = storage { return v }         // hit
            let v = build()                         // miss -> compute
            storage = v
            return v
        }
        // Optional: allow manual set to override cache
        set { storage = newValue }
    }

    struct Controls {
        private let getter: () -> Bool
        private let clearer: () -> Void
        var isCached: Bool { getter() }
        func clear() { clearer() }
    }

    var projectedValue: Controls {
        Controls(
            getter: { self.storage != nil },
            clearer: { self.storage = nil }
        )
    }

    init(_ build: @escaping () -> Value) {
        self.build = build
        self.storage = nil
    }
}

// Demo: expensive computation (simulated)
var calls = 0
func heavy() -> Int { calls += 1; return (1...10_000).reduce(0, +) }

struct Engine {
    @LazyCached(heavy) var result: Int
}
var e = Engine()
print(e.$result.isCached) // false
print(e.result)           // computes once
print(e.result)           // uses cache
print(calls)              // 1

Almacenamiento en caché basado en TTL

@TimedCache actualiza el valor después de un TTL. El valor proyectado puede exponer age para que los puntos de llamada decidan cuándo actualizarlo.

@propertyWrapper
struct TimedCache<Value> {
    private var storage: Value?
    private var timestamp: Date?
    private let ttl: TimeInterval
    private let build: () -> Value

    var wrappedValue: Value {
        mutating get {
            let now = Date()
            if let t = timestamp, let v = storage, now.timeIntervalSince(t) < ttl {
                return v                              // fresh
            }
            let v = build()                           // refresh
            storage = v
            timestamp = now
            return v
        }
    }

    struct Status { let age: TimeInterval? }
    var projectedValue: Status {
        let age = timestamp.map { Date().timeIntervalSince($0) }
        return Status(age: age)
    }

    init(ttl: TimeInterval, _ build: @escaping () -> Value) {
        self.ttl = ttl
        self.build = build
    }
}

// Demo (age only; TTL behavior depends on real time)
struct Stats {
    @TimedCache(ttl: 60) { Int.random(in: 0...1000) } var score: Int
}
var s2 = Stats()
print(s2.score)         // computes
print(s2.$score.age ?? -1)

Pautas de diseño

Consejos de diseño:

  • Mantenga la validación pura y predecible; informe de las correcciones mediante $flags.
  • Para el almacenamiento en caché, mantenga el estado reducido; proporcione clear() o isCached.
  • Prefiera argumentos de inicialización para las políticas (valores alternativos, TTL).

Helpers proyectados para el almacenamiento en caché

Comprobación rápida: ¿Qué debería exponer $value en un wrapper de almacenamiento en caché?

Repaso

Repaso: Use wrappers para validar entradas (valores predeterminados, reglas) y para almacenar en caché resultados costosos (de forma diferida, con TTL). Incluya el control y el estado en projectedValue para mantener limpios los puntos de llamada.

Preguntas frecuentes

¿La lección «Patrones comunes de wrappers (validación, caché)» es gratis?

Sí — el texto completo de «Patrones comunes de wrappers (validación, caché)» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Swift Academy, actualiza a CoddyKit PRO. El curso de Swift Academy incluye 3 lecciones en total.

¿Qué aprenderé en «Patrones comunes de wrappers (validación, caché)»?

Cree wrappers habituales de validation y caching : valide la entrada, proporcione valores predeterminados, memorice operaciones costosas y exponga indicadores auxiliares mediante valores proyectados. Practicas Swift Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Swift Academy?

No se requiere experiencia previa. Swift Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 3.

¿Cuánto tiempo toma la lección «Patrones comunes de wrappers (validación, caché)»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Swift Academy?

Sí. Cada lección de Swift Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Crear wrappers, projectedValue
  2. Reutilización y restricciones
  3. Patrones comunes de wrappers (validación, caché)
← Volver a Swift Academy