Skip to content

Instantly share code, notes, and snippets.

@douglashill
Created April 22, 2020 08:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save douglashill/accde7f3db06b33a37f1389e32d293b1 to your computer and use it in GitHub Desktop.
Save douglashill/accde7f3db06b33a37f1389e32d293b1 to your computer and use it in GitHub Desktop.
A Swift property wrapper that implements ‘lazy let’. I.e. a read-only property that loads its value when first read. Not safe for access from multiple threads.
/// A Swift property wrapper that implements ‘lazy let’. I.e. a read-only property that loads its value when first read.
/// Not safe for access from multiple threads.
/// Does not work the same as the lazy keyboard because the property initialiser will run before self is available.
/// Adapted from https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md
@propertyWrapper enum LazyLet<Value> {
case uninitialised(() -> Value)
case initialised(Value)
init(wrappedValue: @autoclosure @escaping () -> Value) {
self = .uninitialised(wrappedValue)
}
var wrappedValue: Value {
mutating get {
switch self {
case .uninitialised(let initialiser):
let value = initialiser()
self = .initialised(value)
return value
case .initialised(let value):
return value
}
}
}
}
@danyowdee
Copy link

danyowdee commented Apr 22, 2020

What about using a class instead? The following one should solve your case:

@propertyWrapper
class DependentLazyLet<Dependency: AnyObject, Value> {
    typealias Generator = (Dependency) -> Value
    private weak var dependency: Dependency?
    private var value: Value?
    private let generator: Generator

    init(generator: @escaping Generator) {
        self.generator = generator
    }

    var wrappedValue: Value {
        if let value = value {
            return value
        }
        guard let dependency = dependency else {
            preconditionFailure("Cannot generate value without dependency. Did you forget to call resolve(dependency:) in your initialiser(s)?")
        }
        value = generator(dependency)

        return value!
    }

    func resolve(dependency: Dependency) {
        precondition(self.dependency == nil, "Must not resolve twice")
        self.dependency = dependency
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment