A few ways to have a lazily-initialized value in Swift 5.1. Note that, if you are OK with the behavior of Swift’s lazy keyword, you should use that. This is for those who want very specific behaviors:
Lazy: A non-resettable lazy pattern, to guarantee lazy behavior across Swift language versions
ResettableLazy: A resettable lazy pattern, whose value is generated and cached only when first needed, and can be destroyed when no longer needed.
FunctionalLazy: An idea about how to approach the lazy pattern by using functions instead of branches.
Automatic Conformance
The built-in containers (Lazy, ResettableLazy, and FunctionalLazy) automatically conform to Equatable, Hashable, Encodable, and Decodable when their values conform do too! This is a passthrough conformance, simply calling the functions of the wrapped value.
Keep in mind, though, that in order to do this, the value is automatically initialized and accessed!
Compatibility Notice
The entire repository structure had to be changed in order to be compatible with Swift Package Manager (#4). Because of this, the API version changed from 2.0.0 to 3.0.0. Very little of the actual API changed along with this (#8); it was almost entirely to service Swift Package manager.
In version 2.0.0, this readme recommended that you change any reference to ./Lazy.swift to ./LazyContainers/Sources/LazyContainers/LazyContainers.swift. Unfortunately, that wasn’t compatible with Swift Package Manager, so ./Lazy.swift was changed to ./Sources/LazyContainers/LazyContainers.swift. Because of this, please change any reference to ./LazyContainers/Sources/LazyContainers/LazyContainers.swift to ./Sources/LazyContainers/LazyContainers.swift. Sorry about that 🤷🏽
Examples
It’s easy to use each of these. Simply place the appropriate one as a property wrapper where you want it.
Lazy
The simple usage of this is very straightforward:
@Lazy
var myLazyString = "Hello, lazy!"
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
This will print:
Hello, lazy!
Hello, lazy!
Overwritten
Overwritten
More complex initializer
If you have complex initializer logic, you can pass that to the property wrapper:
func makeLazyString() -> String {
print("Initializer side-effect")
return "Hello, lazy!"
}
@Lazy(initializer: makeLazyString)
var myLazyString: String
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
You can also use it directly (instaed of as a property wrapper):
var myLazyString = Lazy<String>() {
print("Initializer side-effect")
return "Hello, lazy!"
}
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.wrappedValue = "Overwritten"
print(myLazyString.wrappedValue) // Just returns the value "Overwritten"
print(myLazyString.wrappedValue) // Just returns the value "Overwritten"
@ResettableLazy
var myLazyString = "Hello, lazy!"
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
If you have complex initializer logic, you can pass that to the property wrapper:
func makeLazyString() -> String {
print("Initializer side-effect")
return "Hello, lazy!"
}
@ResettableLazy(initializer: makeLazyString)
var myLazyString: String
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
You can also use it directly (instaed of as a property wrapper):
var myLazyString = ResettableLazy<String>() {
print("Initializer side-effect")
return "Hello, lazy!"
}
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.wrappedValue = "Overwritten"
print(myLazyString.wrappedValue) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
Swift Lazy Containers
A few ways to have a lazily-initialized value in Swift 5.1. Note that, if you are OK with the behavior of Swift’s
lazy
keyword, you should use that. This is for those who want very specific behaviors:Lazy
: A non-resettable lazy pattern, to guarantee lazy behavior across Swift language versionsResettableLazy
: A resettable lazy pattern, whose value is generated and cached only when first needed, and can be destroyed when no longer needed.FunctionalLazy
: An idea about how to approach the lazy pattern by using functions instead of branches.Automatic Conformance
The built-in containers (
Lazy
,ResettableLazy
, andFunctionalLazy
) automatically conform toEquatable
,Hashable
,Encodable
, andDecodable
when their values conform do too! This is a passthrough conformance, simply calling the functions of the wrapped value.Keep in mind, though, that in order to do this, the value is automatically initialized and accessed!
Compatibility Notice
The entire repository structure had to be changed in order to be compatible with Swift Package Manager (#4). Because of this, the API version changed from 2.0.0 to 3.0.0. Very little of the actual API changed along with this (#8); it was almost entirely to service Swift Package manager.
In version 2.0.0, this readme recommended that you change any reference to
./Lazy.swift
to./LazyContainers/Sources/LazyContainers/LazyContainers.swift
. Unfortunately, that wasn’t compatible with Swift Package Manager, so./Lazy.swift
was changed to./Sources/LazyContainers/LazyContainers.swift
. Because of this, please change any reference to./LazyContainers/Sources/LazyContainers/LazyContainers.swift
to./Sources/LazyContainers/LazyContainers.swift
. Sorry about that 🤷🏽Examples
It’s easy to use each of these. Simply place the appropriate one as a property wrapper where you want it.
Lazy
The simple usage of this is very straightforward:
This will print:
More complex initializer
If you have complex initializer logic, you can pass that to the property wrapper:
You can also use it directly (instaed of as a property wrapper):
These will both print:
ResettableLazy
The simple usage of this is very straightforward:
This will print:
More complex initializer
If you have complex initializer logic, you can pass that to the property wrapper:
You can also use it directly (instaed of as a property wrapper):
These will both print:
FunctionalLazy
This is functionally (ha!) the same as
Lazy
. The only difference is I thought it’d be fun to implement it with functions instead of enums. 🤓