Extends PR #3057 to include initial values in addition to default values.
Persistent Shared values are often declared with an initial value.
@Shared(.appStorage("key")) var value = 0
This initial value is eagerly evaluated and passed to the Shared initializer. When there's an existing reference, this initial value is thrown away. For simple types like the one above, this isn't a problem. However, when the initial value is more complex, it can be wasteful if the Shared value is declared in many features of an app.
More significantly, the initial value may have side effects. For example, consider a Folder model that creates a UUID id using the uuid dependency. If you want to persist the Folder, you would declare a PersistenceKey.
extension PersistenceKey where Self == FileStorageKey<Folder> {
public static var rootFolder: Self {
fileStorage(.documentsDirectory.appendingPathComponent("rootFolder", conformingTo: .json))
}
}
Your feature declares the shared folder in its state.
Because the Feature.State initializer evaluated the Folder declaration, the uuid was incremented. The value was thrown away because the Shared value in the dependencies block already created the reference, but the side effect still happened.
This PR makes that initial value load lazily so there are no side effects unless the value is actually needed. State initializers and local declarations are more efficient, and tests are deterministic.
Extends PR #3057 to include initial values in addition to default values.
Persistent Shared values are often declared with an initial value.
This initial value is eagerly evaluated and passed to the Shared initializer. When there's an existing reference, this initial value is thrown away. For simple types like the one above, this isn't a problem. However, when the initial value is more complex, it can be wasteful if the Shared value is declared in many features of an app.
More significantly, the initial value may have side effects. For example, consider a
Folder
model that creates aUUID
id using theuuid
dependency. If you want to persist theFolder
, you would declare aPersistenceKey
.Your feature declares the shared folder in its state.
Now you want to override that root folder in a test.
Because the
Feature.State
initializer evaluated theFolder
declaration, theuuid
was incremented. The value was thrown away because the Shared value in the dependencies block already created the reference, but the side effect still happened.This PR makes that initial value load lazily so there are no side effects unless the value is actually needed. State initializers and local declarations are more efficient, and tests are deterministic.