Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy loading of Shared initial values #3060

Merged
merged 1 commit into from
May 9, 2024
Merged

Conversation

seanmrich
Copy link
Contributor

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.

@ObservableState
struct State: Equatable {
  @Shared(.rootFolder) var root = Folder()
}

Now you want to override that root folder in a test.

func testAddFolder() async {
  let store = TestStore(
    initialState: Feature.State(),
    reducer: { Feature() },
    withDependencies: {
      $0.uuid = .incrementing
      @Shared(.rootFolder) var root = Folder()
    }
  )
  await store.send(.addFolderButtonTapped) {
    $0.root.subfolders = [
      Folder(id: UUID(???))
    ]
  }
}

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.

Copy link
Member

@stephencelis stephencelis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this feature to the finish line!

@stephencelis stephencelis merged commit f248eff into pointfreeco:main May 9, 2024
7 checks passed
cgrindel-self-hosted-renovate bot added a commit to cgrindel/rules_swift_package_manager that referenced this pull request May 13, 2024
…ure to from: "1.10.4" (#1067)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[pointfreeco/swift-composable-architecture](https://togithub.com/pointfreeco/swift-composable-architecture)
| patch | `from: "1.10.3"` -> `from: "1.10.4"` |

---

### Release Notes

<details>
<summary>pointfreeco/swift-composable-architecture
(pointfreeco/swift-composable-architecture)</summary>

###
[`v1.10.4`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/1.10.4)

[Compare
Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/1.10.3...1.10.4)

#### What's Changed

- Fixed: Compute warnings lazily by
[@&#8203;heoblitz](https://togithub.com/heoblitz) in
[pointfreeco/swift-composable-architecture#3058
- Fixed: Lazy loading of Shared initial values by
[@&#8203;seanmrich](https://togithub.com/seanmrich) in
[pointfreeco/swift-composable-architecture#3060
- Fixed: Fix for throttle logic. by
[@&#8203;mbrandonw](https://togithub.com/mbrandonw) in
[pointfreeco/swift-composable-architecture#3075

#### New Contributors

- [@&#8203;heoblitz](https://togithub.com/heoblitz) made their first
contribution in
[pointfreeco/swift-composable-architecture#3058

**Full Changelog**:
pointfreeco/swift-composable-architecture@1.10.3...1.10.4

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://togithub.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi4xMDkuNCIsInVwZGF0ZWRJblZlciI6IjM2LjEwOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9-->

Co-authored-by: Self-hosted Renovate Bot <361546+cgrindel-self-hosted-renovate[bot]@users.noreply.github.enterprise.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants