0Pricing
Swift Academy · レッスン

よく使うラッパーのパターン(検証、キャッシュ)

一般的な 検証 ラッパーと キャッシュ ラッパーを構築します。入力をガードし、デフォルト値を提供し、コストの高い処理をメモ化して、projected valueでヘルパーフラグを公開します。

「よく使うラッパーのパターン(検証、キャッシュ)」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全3レッスンが含まれています。

作成するもの

このレッスンでは、次の2つの実用的なパターンを扱います。

  • バリデーション: デフォルト値とメッセージを使って、入力を受け入れるか修正します。
  • キャッシュ: 結果を保存し、$helpersとしてclear()やisCachedを公開します。

バリデーション: NonEmpty

@NonEmptyは空の入力を防ぎ、デフォルト値を提供します。projectedValueは、最後に設定した値が自動修正されたかどうかを報告します。

@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)

バリデーション: ルールベース

@Validatedはジェネリックで、ルール用のクロージャとフォールバックを受け取ります。長さ、範囲、カスタムチェックに使用できます。

@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

キャッシュ: 遅延メモ化

@LazyCachedはクロージャから値をメモ化します。$resultはisCachedと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

キャッシュ: TTLベース

@TimedCacheはTTLの経過後に更新します。射影値でageを公開すれば、呼び出し側で更新するタイミングを判断できます。

@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)

設計ガイドライン

設計のヒント:

  • バリデーションは純粋で予測可能にし、修正内容は$flagsで報告します。
  • キャッシュでは状態を小さく保ち、clear()またはisCachedを提供します。
  • ポリシー(フォールバック、TTL)にはinitの引数を優先します。

キャッシュ用の射影ヘルパー

確認問題: キャッシュ用ラッパーの$valueには何を公開すべきでしょうか?

まとめ

まとめ: ラッパーを使って入力を検証し(デフォルト値、ルール)、コストの高い結果をキャッシュします(遅延、TTL)。制御や状態はprojectedValueにまとめ、呼び出し側をすっきりさせます。

よくある質問

「よく使うラッパーのパターン(検証、キャッシュ)」レッスンは無料ですか?

はい。「よく使うラッパーのパターン(検証、キャッシュ)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全3レッスンが含まれています。

「よく使うラッパーのパターン(検証、キャッシュ)」で何を学びますか?

一般的な 検証 ラッパーと キャッシュ ラッパーを構築します。入力をガードし、デフォルト値を提供し、コストの高い処理をメモ化して、projected valueでヘルパーフラグを公開します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。

「よく使うラッパーのパターン(検証、キャッシュ)」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ラッパーとprojectedValueの作成
  2. 再利用性と制約
  3. よく使うラッパーのパターン(検証、キャッシュ)
← Swift Academyに戻る