-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Avoid force-unwrapping cached value in ForEachStore #1036
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
Avoid force-unwrapping cached value in ForEachStore #1036
Conversation
|
@ohitsdaniel Thanks for taking the time to PR! Can you share a repro of the race condition? We have to be very careful when making changes to I think we'll want to take a little time with this PR to ensure we don't introduce any performance regressions, but it would help to also have a reproducible crash, should we need to find an alternate fix. |
|
Thanks for the feedback! I can also see some issues with the content of the ForEach view becoming optional (an optional resolves to an EmptyView in a ViewBuilder, so it's still counted as an element and could lead to some empty rows). I'll try to come up with a small example application that reproduces the crash. My assumption is that it has something to do with a |
|
@ohitsdaniel This PR does unfortunately regress some behavior, where each row's view is initialized eagerly, even if the row is not in view. There was an issue/discussion about this, and a fix, but I wasn't able to quickly dig it up. The easiest way to see the problem might be in the Todos project. On your branch:
You'll notice If you switch over to Initializing views should be very lightweight, so this could be a trade-off to take on if the problem is big enough. However, we also worry that incurring this amount of initialization could be quite bad for very large lists. Ideally there's another workaround to the problem. |
|
Thanks for this easy way to test if the new behaviour matches the old one. I think the reason for the crash mentioned in the feedback is By introducing a If introducing a |
|
We managed to reproduce the crash reliably. To reproduceApply the diff below to the example TCA app, Todos. Diff diff --git a/Examples/Todos/Todos/Todos.swift b/Examples/Todos/Todos/Todos.swift
index 6945fa2..cca92aa 100644
--- a/Examples/Todos/Todos/Todos.swift
+++ b/Examples/Todos/Todos/Todos.swift
@@ -21,6 +21,9 @@ struct AppState: Equatable {
}
}
+let crazyNumberOfTodos = 100
+let crazyNumberOfTimes : UInt = 40
+
enum AppAction: Equatable {
case addTodoButtonTapped
case clearCompletedButtonTapped
@@ -30,6 +33,7 @@ enum AppAction: Equatable {
case move(IndexSet, Int)
case sortCompletedTodos
case todo(id: Todo.ID, action: TodoAction)
+ case goCrazy(howManyTimes: UInt)
}
struct AppEnvironment {
@@ -94,10 +98,22 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
case .todo:
return .none
+
+ case let .goCrazy(howManyTimes):
+ state.todos = []
+ if howManyTimes > 0 {
+ for _ in 1 ... crazyNumberOfTodos {
+ state.todos.insert(Todo(id: environment.uuid(), isComplete: true), at: 0)
+ }
+ return Effect(value: .goCrazy(howManyTimes: howManyTimes - 1))
+ .receive(on: environment.mainQueue)
+ .eraseToEffect()
+ } else {
+ return .none
+ }
}
}
)
-.debug()
struct AppView: View {
let store: Store<AppState, AppAction>
@@ -147,10 +163,9 @@ struct AppView: View {
.navigationBarItems(
trailing: HStack(spacing: 20) {
EditButton()
- Button("Clear Completed") {
- self.viewStore.send(.clearCompletedButtonTapped, animation: .default)
+ Button("Go Crazy") {
+ self.viewStore.send(.goCrazy(howManyTimes: crazyNumberOfTimes))
}
- .disabled(self.viewStore.isClearCompletedButtonDisabled)
Button("Add Todo") { self.viewStore.send(.addTodoButtonTapped, animation: .default) }
}
) |
| self.content = { | ||
| WithViewStore(store.scope(state: { $0.ids })) { viewStore in | ||
| ForEach(viewStore.state, id: \.self) { id -> EachContent in | ||
| WithViewStore(store) { viewStore in | ||
| ForEach(viewStore.state, id: viewStore.state.id) { element in | ||
| // NB: We cache elements here to avoid a potential crash where SwiftUI may re-evaluate | ||
| // views for elements no longer in the collection. | ||
| // | ||
| // Feedback filed: https://gist.github.com/stephencelis/cdf85ae8dab437adc998fb0204ed9a6b | ||
| var element = store.state.value[id: id]! | ||
| return content( | ||
| let id = element[keyPath: viewStore.state.id] | ||
| content( | ||
| store.scope( | ||
| state: { | ||
| element = $0[id: id] ?? element | ||
| return element | ||
| }, | ||
| state: { $0[id: id] ?? element }, | ||
| action: { (id, $0) } | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ohitsdaniel What if we have an outer fallback cache, as well? Does this fix things or cause any weird behavior?
var outer: EachState?
self.data = store.state.value
self.content = {
WithViewStore(store.scope(state: { $0.ids })) { viewStore in
ForEach(viewStore.state, id: \.self) { id -> EachContent in
// NB: We cache elements here to avoid a potential crash where SwiftUI may re-evaluate
// views for elements no longer in the collection.
//
// Feedback filed: https://gist.github.com/stephencelis/cdf85ae8dab437adc998fb0204ed9a6b
var inner = store.state.value[id: id] ?? outer!
outer = inner
return content(
store.scope(
state: {
inner = $0[id: id] ?? inner
return inner
},
action: { (id, $0) }
)
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Stephen,
Your fix works for my example! The race condition is reduced. But it still breaks reliably in a more complicated example.
With a class, it can happen that an IdentifiedArray is modified during view execution. This is of course not a good pattern, ideally devs only use structs in their states, but some types from libraries might be funny classes and outside of the control of developer. I guess TCA should be robust to that.
The repro makes lots of 1-element IdentifiedArrays, each todo inside a separate IdentifiedArray, and it tries to empty some of the inner arrays before the corresponding view can be evaluated.
I show the repro in two parts.
Repro diff 1/2 from upstream TCA: core stuff
diff --git a/Examples/Todos/Todos/Todo.swift b/Examples/Todos/Todos/Todo.swift
index 327b7a5d8..279e38363 100644
--- a/Examples/Todos/Todos/Todo.swift
+++ b/Examples/Todos/Todos/Todo.swift
@@ -2,6 +2,22 @@ import ComposableArchitecture
import Foundation
import SwiftUI
+class IdentifiedCell<T> : Equatable, Identifiable where T: Equatable {
+ public var id: UUID
+ public var value: T
+ public var pointer: IdentifiedCell<T>?
+
+ init(_ value: T, pointer: IdentifiedCell<T>? = .none) {
+ self.id = .init()
+ self.value = value
+ self.pointer = pointer
+ }
+
+ static func ==(lhs: IdentifiedCell<T>, rhs: IdentifiedCell<T>) -> Bool {
+ return lhs.id == rhs.id && lhs.value == rhs.value
+ }
+}
+
struct Todo: Equatable, Identifiable {
var description = ""
let id: UUID
diff --git a/Examples/Todos/Todos/Todos.swift b/Examples/Todos/Todos/Todos.swift
index 6945fa294..bd2b618ac 100644
--- a/Examples/Todos/Todos/Todos.swift
+++ b/Examples/Todos/Todos/Todos.swift
@@ -7,10 +7,12 @@ enum Filter: LocalizedStringKey, CaseIterable, Hashable {
case completed = "Completed"
}
+typealias IdentifiedCellTodoArray = IdentifiedCell<IdentifiedArrayOf<Todo>>
+
struct AppState: Equatable {
var editMode: EditMode = .inactive
var filter: Filter = .all
- var todos: IdentifiedArrayOf<Todo> = []
+ var todos: IdentifiedArrayOf<IdentifiedCellTodoArray> = []
var filteredTodos: IdentifiedArrayOf<Todo> {
switch filter {
@@ -21,6 +23,9 @@ struct AppState: Equatable {
}
}
+let crazyNumberOfTodos = 100
+let crazyNumberOfTimes = 40
+
enum AppAction: Equatable {
case addTodoButtonTapped
case clearCompletedButtonTapped
@@ -30,6 +35,7 @@ enum AppAction: Equatable {
case move(IndexSet, Int)
case sortCompletedTodos
case todo(id: Todo.ID, action: TodoAction)
+ case goCrazy(howManyTimes: UInt)
}
struct AppEnvironment {
@@ -94,10 +100,25 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
case .todo:
return .none
+
+ case let .goCrazy(howManyTimes):
+ state.todos = []
+ if howManyTimes > 0 {
+ var next : IdentifiedCellTodoArray?
+ for i in 0 ..< crazyNumberOfTodos {
+ let todo = Todo(description: "\(howManyTimes) / \(i)", id: environment.uuid(), isComplete: true)
+ next = .init([todo], pointer: next)
+ state.todos.insert(next!, at: 0)
+ }
+ return Effect(value: .goCrazy(howManyTimes: howManyTimes - 1))
+ .receive(on: environment.mainQueue)
+ .eraseToEffect()
+ } else {
+ return .none
+ }
}
}
)
-.debug()
struct AppView: View {
let store: Store<AppState, AppAction>
@@ -135,10 +156,19 @@ struct AppView: View {
.padding(.horizontal)
List {
- ForEachStore(
- self.store.scope(state: \.filteredTodos, action: AppAction.todo(id:action:)),
- content: TodoView.init(store:)
- )
+ ForEachStore(self.store.scope(
+ state: \.todos,
+ action: AppAction.todo(id:action:))) { firstStore in
+ WithViewStore(firstStore) { viewStore in
+ ForEachStore(firstStore.scope(
+ state: \.value,
+ action: TodoActionWrapper.Wrap(id:action:))) { secondStore in
+ // On every second TodoView, this quickly removes the next Todo
+ let _ = viewStore.state.pointer?.value = []
+ TodoView.init(store: secondStore)
+ }
+ }
+ }
.onDelete { self.viewStore.send(.delete($0)) }
.onMove { self.viewStore.send(.move($0, $1)) }
}
@@ -147,10 +177,9 @@ struct AppView: View {
.navigationBarItems(
trailing: HStack(spacing: 20) {
EditButton()
- Button("Clear Completed") {
- self.viewStore.send(.clearCompletedButtonTapped, animation: .default)
+ Button("Go Crazy") {
+ self.viewStore.send(.goCrazy(howManyTimes: crazyNumberOfTimes))
}
- .disabled(self.viewStore.isClearCompletedButtonDisabled)
Button("Add Todo") { self.viewStore.send(.addTodoButtonTapped, animation: .default) }
}
)Repro diff 2/2 from upstream TCA: support
diff --git a/Examples/Todos/Todos/Todos.swift b/Examples/Todos/Todos/Todos.swift
index bd2b618ac..5ccef637d 100644
--- a/Examples/Todos/Todos/Todos.swift
+++ b/Examples/Todos/Todos/Todos.swift
@@ -14,11 +14,16 @@ struct AppState: Equatable {
var filter: Filter = .all
var todos: IdentifiedArrayOf<IdentifiedCellTodoArray> = []
+ var flatTodos: IdentifiedArrayOf<Todo> {
+ let elements = self.todos.elements.flatMap { inner in inner.value.elements };
+ return IdentifiedArrayOf.init(uniqueElements: elements)
+ }
+
var filteredTodos: IdentifiedArrayOf<Todo> {
switch filter {
- case .active: return self.todos.filter { !$0.isComplete }
- case .all: return self.todos
- case .completed: return self.todos.filter(\.isComplete)
+ case .active: return self.flatTodos.filter { !$0.isComplete }
+ case .all: return self.flatTodos
+ case .completed: return self.flatTodos.filter(\.isComplete)
}
}
}
@@ -34,8 +39,12 @@ enum AppAction: Equatable {
case filterPicked(Filter)
case move(IndexSet, Int)
case sortCompletedTodos
- case todo(id: Todo.ID, action: TodoAction)
- case goCrazy(howManyTimes: UInt)
+ case todo(id: Todo.ID, action: TodoActionWrapper)
+ case goCrazy(howManyTimes: Int)
+}
+
+enum TodoActionWrapper: Equatable {
+ case Wrap(id: Todo.ID, action: TodoAction)
}
struct AppEnvironment {
@@ -45,6 +54,10 @@ struct AppEnvironment {
let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
todoReducer.forEach(
+ state: \.value,
+ action: /TodoActionWrapper.Wrap(id:action:),
+ environment: { _ in TodoEnvironment() }
+ ).forEach(
state: \.todos,
action: /AppAction.todo(id:action:),
environment: { _ in TodoEnvironment() }
@@ -52,11 +65,10 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
Reducer { state, action, environment in
switch action {
case .addTodoButtonTapped:
- state.todos.insert(Todo(id: environment.uuid()), at: 0)
+ state.todos.insert(.init([Todo(id: environment.uuid())]), at: 0)
return .none
case .clearCompletedButtonTapped:
- state.todos.removeAll(where: \.isComplete)
return .none
case let .delete(indexSet):
@@ -90,14 +102,8 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
.eraseToEffect()
case .sortCompletedTodos:
- state.todos.sort { $1.isComplete && !$0.isComplete }
return .none
- case .todo(id: _, action: .checkBoxToggled):
- struct TodoCompletionId: Hashable {}
- return Effect(value: .sortCompletedTodos)
- .debounce(id: TodoCompletionId(), for: 1, scheduler: environment.mainQueue.animation())
-
case .todo:
return .none
@@ -137,7 +143,7 @@ struct AppView: View {
init(state: AppState) {
self.editMode = state.editMode
self.filter = state.filter
- self.isClearCompletedButtonDisabled = !state.todos.contains(where: \.isComplete)
+ self.isClearCompletedButtonDisabled = !state.flatTodos.contains(where: \.isComplete)
}
}
@@ -192,31 +198,33 @@ struct AppView: View {
}
}
-extension IdentifiedArray where ID == Todo.ID, Element == Todo {
- static let mock: Self = [
- Todo(
- description: "Check Mail",
- id: UUID(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEDDEADBEEF")!,
- isComplete: false
- ),
- Todo(
- description: "Buy Milk",
- id: UUID(uuidString: "CAFEBEEF-CAFE-BEEF-CAFE-BEEFCAFEBEEF")!,
- isComplete: false
- ),
- Todo(
- description: "Call Mom",
- id: UUID(uuidString: "D00DCAFE-D00D-CAFE-D00D-CAFED00DCAFE")!,
- isComplete: true
- ),
- ]
-}
+let mock: [Todo] = [
+ Todo(
+ description: "Check Mail",
+ id: UUID(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEDDEADBEEF")!,
+ isComplete: false
+ ),
+ Todo(
+ description: "Buy Milk",
+ id: UUID(uuidString: "CAFEBEEF-CAFE-BEEF-CAFE-BEEFCAFEBEEF")!,
+ isComplete: false
+ ),
+ Todo(
+ description: "Call Mom",
+ id: UUID(uuidString: "D00DCAFE-D00D-CAFE-D00D-CAFED00DCAFE")!,
+ isComplete: true
+ ),
+]
struct AppView_Previews: PreviewProvider {
+ static let todos = IdentifiedArrayOf.init(uniqueElements: mock.map {
+ todo in IdentifiedCell(IdentifiedArrayOf.init(uniqueElements: [todo]))
+ })
+
static var previews: some View {
AppView(
store: Store(
- initialState: AppState(todos: .mock),
+ initialState: AppState(todos: todos),
reducer: appReducer,
environment: AppEnvironment(
mainQueue: .main,|
Hey guys 👋 I am able to reproduce the crash on my side, but still trying to figure out a reasonably sized example I could provide here. My app crashes when using a search bar to filter out a Adding a small delay (10ms) before loading the new state fixes it, but ofc I'm not happy with that solution. 😀 If you have any tips how to proceed, I'd be thankful. 😇 |
|
Hey @iblagajic! .package(url: "https://github.com/tgrapperon/swift-composable-architecture", branch: "generic-foreach"),It should build without touching anything hopefully. |
|
@tgrapperon thanks for the reply! 🙇 Unfortunately (or fortunately?), since I reset the cache to switch to your branch, I can't reproduce it on either branch anymore ( |
|
@iblagajic It is also possible that you hit this crash if this is something fixed when building from a clean state. |
|
@tgrapperon Thanks for the heads up. Unfortunately, I was able to reproduce this crash again on 0.44.1 and this time I tried to check out your branch too. However, it still crashes ( |
|
@iblagajic. Yes, this is indeed the equivalent of the force-unwrap. It means that no data can be found for the given |
stephencelis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for sitting on this for so long, but just revisited and rebased and think it's a good change, thanks!
…ure to from: "0.55.0" (#452) [](https://renovatebot.com) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [pointfreeco/swift-composable-architecture](https://togithub.com/pointfreeco/swift-composable-architecture) | minor | `from: "0.51.0"` -> `from: "0.55.0"` | --- ### Release Notes <details> <summary>pointfreeco/swift-composable-architecture (pointfreeco/swift-composable-architecture)</summary> ### [`v0.55.0`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.55.0) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.54.1...0.55.0) #### What's Changed - Added: The Composable Architecture's SwiftUI bindings integration has been greatly improved, with better support for view state bindings ([https://github.com/pointfreeco/swift-composable-architecture/pull/2215](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2215)). - Added: `Store.send` and `Store.withState` have been added, for sending actions to stores and accessing store state without needing a view store ([https://github.com/pointfreeco/swift-composable-architecture/pull/2222](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2222)). - Added: `ReducerProtocol.onChange` ([https://github.com/pointfreeco/swift-composable-architecture/pull/2226](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2226)). - Added: `EffectOf<Reducer>` convenience type alias to `EffectTask<Reducer.Action>` ([https://github.com/pointfreeco/swift-composable-architecture/pull/2237](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2237)). - Updated: `swiftui-navigation` has been bumped to 0.8.0 ([https://github.com/pointfreeco/swift-composable-architecture/pull/2239](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2239)). - Improved: `TestStore` failure messages have been improved ([https://github.com/pointfreeco/swift-composable-architecture/pull/2227](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2227), [https://github.com/pointfreeco/swift-composable-architecture/pull/2236](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2236)). - Fixed: `ForEachStore` no longer force-unwraps its cached value, avoiding crashes in race condition-heavy code (thanks [@​ohitsdaniel](https://togithub.com/ohitsdaniel), [https://github.com/pointfreeco/swift-composable-architecture/pull/1036](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1036)). - Fixed: Addressed a few Xcode 15 warnings (Swift 6 errors) ([https://github.com/pointfreeco/swift-composable-architecture/pull/2213](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2213)). - Deprecated: `Effect.cancel(ids:)` has been deprecated ([https://github.com/pointfreeco/swift-composable-architecture/pull/2221](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2221)). - Infrastructure: Documentation improvements (thanks [@​ccxla](https://togithub.com/ccxla), [https://github.com/pointfreeco/swift-composable-architecture/pull/2185](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2185), [https://github.com/pointfreeco/swift-composable-architecture/pull/2184](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2184), [https://github.com/pointfreeco/swift-composable-architecture/pull/2183](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2183); [@​tomu28](https://togithub.com/tomu28), [https://github.com/pointfreeco/swift-composable-architecture/pull/2209](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2209); [@​alexhunsley](https://togithub.com/alexhunsley), [https://github.com/pointfreeco/swift-composable-architecture/pull/2204](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2204); [@​oronbz](https://togithub.com/oronbz), [https://github.com/pointfreeco/swift-composable-architecture/pull/2173](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2173); [https://github.com/pointfreeco/swift-composable-architecture/pull/2225](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2225); [https://github.com/pointfreeco/swift-composable-architecture/pull/2238](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2238)). #### New Contributors - [@​tomu28](https://togithub.com/tomu28) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2209](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2209) - [@​alexhunsley](https://togithub.com/alexhunsley) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2204](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2204) - [@​ohitsdaniel](https://togithub.com/ohitsdaniel) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/1036](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1036) **Full Changelog**: pointfreeco/swift-composable-architecture@0.54.1...0.55.0 ### [`v0.54.1`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.54.1) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.54.0...0.54.1) #### What's Changed - Fixed: A regression introduced in 0.54.0 prevented some Composable Architecture projects from building in release due to a Swift compiler bug. We have removed some `@inlineable` attributes to work around this bug ([https://github.com/pointfreeco/swift-composable-architecture/pull/2201](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2201)). While we don't anticipate noticeable runtime performance regressions in release builds of applications, please report any issues you may see. - Infrastructure: Added Hindi (Indian) translation of README.md (thanks [@​akashsoni01](https://togithub.com/akashsoni01), [https://github.com/pointfreeco/swift-composable-architecture/pull/2171](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2171)) - Infrastructure: Documentation updates and fixes (thanks [@​Sajjon](https://togithub.com/Sajjon), [https://github.com/pointfreeco/swift-composable-architecture/pull/2150](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2150); [@​tatsuz0u](https://togithub.com/tatsuz0u), [https://github.com/pointfreeco/swift-composable-architecture/pull/2155](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2155); [@​hmhv](https://togithub.com/hmhv), [https://github.com/pointfreeco/swift-composable-architecture/pull/2152](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2152); [@​MarshalGeazipp](https://togithub.com/MarshalGeazipp), [https://github.com/pointfreeco/swift-composable-architecture/pull/2154](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2154); [@​Ryu0118](https://togithub.com/Ryu0118), [https://github.com/pointfreeco/swift-composable-architecture/pull/2153](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2153); [@​Czajnikowski](https://togithub.com/Czajnikowski), [https://github.com/pointfreeco/swift-composable-architecture/pull/2157](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2157); [@​kristofferjohansson](https://togithub.com/kristofferjohansson), [https://github.com/pointfreeco/swift-composable-architecture/pull/2159](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2159); [@​jaesung-0o0](https://togithub.com/jaesung-0o0), [https://github.com/pointfreeco/swift-composable-architecture/pull/2160](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2160); [https://github.com/pointfreeco/swift-composable-architecture/pull/2161](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2161); [@​takehilo](https://togithub.com/takehilo), [https://github.com/pointfreeco/swift-composable-architecture/pull/2165](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2165); [@​nickkohrn](https://togithub.com/nickkohrn), [https://github.com/pointfreeco/swift-composable-architecture/pull/2168](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2168) [https://github.com/pointfreeco/swift-composable-architecture/pull/2169](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2169); [@​d-date](https://togithub.com/d-date), [https://github.com/pointfreeco/swift-composable-architecture/pull/2174](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2174); [@​oronbz](https://togithub.com/oronbz), [https://github.com/pointfreeco/swift-composable-architecture/pull/2175](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2175), [https://github.com/pointfreeco/swift-composable-architecture/pull/2176](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2176), [https://github.com/pointfreeco/swift-composable-architecture/pull/2177](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2177); [@​devMinseok](https://togithub.com/devMinseok), [https://github.com/pointfreeco/swift-composable-architecture/pull/2180](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2180); [@​ccxla](https://togithub.com/ccxla), [https://github.com/pointfreeco/swift-composable-architecture/pull/2181](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2181); [@​filblue](https://togithub.com/filblue), [https://github.com/pointfreeco/swift-composable-architecture/pull/2188](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2188); [@​thomastosoni](https://togithub.com/thomastosoni), [https://github.com/pointfreeco/swift-composable-architecture/pull/2190](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2190)). #### New Contributors - [@​Sajjon](https://togithub.com/Sajjon) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2150](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2150) - [@​MarshalGeazipp](https://togithub.com/MarshalGeazipp) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2154](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2154) - [@​takehilo](https://togithub.com/takehilo) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2165](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2165) - [@​nickkohrn](https://togithub.com/nickkohrn) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2168](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2168) - [@​akashsoni01](https://togithub.com/akashsoni01) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2171](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2171) - [@​d-date](https://togithub.com/d-date) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2174](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2174) - [@​oronbz](https://togithub.com/oronbz) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2175](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2175) - [@​devMinseok](https://togithub.com/devMinseok) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2180](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2180) - [@​ccxla](https://togithub.com/ccxla) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2181](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2181) - [@​thomastosoni](https://togithub.com/thomastosoni) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2190](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2190) **Full Changelog**: pointfreeco/swift-composable-architecture@0.54.0...0.54.1 ### [`v0.54.0`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.54.0) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.53.2...0.54.0) #### What's Changed - Added: All-new navigation tools for presenting child features ([https://github.com/pointfreeco/swift-composable-architecture/pull/1945](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1945), [pointfreeco/swift-composable-architecture#1944, [pointfreeco/swift-composable-architecture#2048). See the associated [documentation](https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/navigation) and [tutorial](https://pointfreeco.github.io/swift-composable-architecture/main/tutorials/meetcomposablearchitecture/#navigation) for how to incorporate these tools into your applications today! - Added: `TestStore.assert`, for asserting state changes on non-exhaustive stores at any time ([https://github.com/pointfreeco/swift-composable-architecture/pull/2123](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2123)). - Fixed: Ensure that a test store helper runs on the main actor ([https://github.com/pointfreeco/swift-composable-architecture/pull/2117](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2117)). - Added: Ukrainian translation of TCA's README (thanks [@​barabashd](https://togithub.com/barabashd), [https://github.com/pointfreeco/swift-composable-architecture/pull/2121](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2121)). - Infrastructure: DocC organization ([https://github.com/pointfreeco/swift-composable-architecture/pull/2118](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2118)). - Infrastructure: Ensure CI runs library tests in release ([https://github.com/pointfreeco/swift-composable-architecture/pull/2120](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2120)). - Fix assertion values by [@​tomassliz](https://togithub.com/tomassliz) in [https://github.com/pointfreeco/swift-composable-architecture/pull/2128](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2128) - Infrastructure: Documentation fixes (thanks [@​tomassliz](https://togithub.com/tomassliz), [https://github.com/pointfreeco/swift-composable-architecture/pull/2124](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2124), [https://github.com/pointfreeco/swift-composable-architecture/pull/2128](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2128); [@​jaesung-0o0](https://togithub.com/jaesung-0o0), [https://github.com/pointfreeco/swift-composable-architecture/pull/2144](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2144)). #### New Contributors - [@​tomassliz](https://togithub.com/tomassliz) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2124](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2124) - [@​jaesung-0o0](https://togithub.com/jaesung-0o0) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2144](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2144) **Full Changelog**: pointfreeco/swift-composable-architecture@0.53.2...0.54.0 ### [`v0.53.2`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.53.2) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.53.1...0.53.2) #### What's Changed - Make `Send` sendable ([https://github.com/pointfreeco/swift-composable-architecture/pull/2112](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2112)) - When test exhaustivity is off, `receive` now waits for the expected action rather than taking the first action (thanks [@​alex-reilly-pronto](https://togithub.com/alex-reilly-pronto), [https://github.com/pointfreeco/swift-composable-architecture/pull/2100](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2100)) - Fix typo in the "Meet the Composable Architecture" (thanks [@​redryerye](https://togithub.com/redryerye), [https://github.com/pointfreeco/swift-composable-architecture/pull/2114](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2114)) - Fix compile error in Xcode <14.3 (thanks [@​hj56775](https://togithub.com/hj56775), [https://github.com/pointfreeco/swift-composable-architecture/pull/2115](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2115)) #### New Contributors - [@​alex-reilly-pronto](https://togithub.com/alex-reilly-pronto) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2100](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2100) - [@​redryerye](https://togithub.com/redryerye) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2114](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2114) - [@​hj56775](https://togithub.com/hj56775) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2115](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2115) **Full Changelog**: pointfreeco/swift-composable-architecture@0.53.1...0.53.2 ### [`v0.53.1`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.53.1) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.53.0...0.53.1) #### What's Changed - Fixed: A regression was introduced in 0.53.0 where `TestStore.init`'s `prepareDependencies` was called twice. It will not be called just a single time again ([https://github.com/pointfreeco/swift-composable-architecture/pull/2111](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2111)). - Infrastructure: Added a "Meet the Composable Architecture" tutorial ([https://github.com/pointfreeco/swift-composable-architecture/pull/2107](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2107), [https://github.com/pointfreeco/swift-composable-architecture/pull/2109](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2109)). - Infrastructure: Docs fixes (thanks [@​Ryu0118](https://togithub.com/Ryu0118), [https://github.com/pointfreeco/swift-composable-architecture/pull/2110](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2110)) **Full Changelog**: pointfreeco/swift-composable-architecture@0.53.0...0.53.1 ### [`v0.53.0`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.53.0) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.52.0...0.53.0) #### What's Changed - Added: `Store.init` and `TestStore.init` now take reducer builders ([https://github.com/pointfreeco/swift-composable-architecture/pull/2087](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2087)). ```swift // Before: Store( initialState: Feature.State(), reducer: Feature() ) // After: Store(initialState: Feature.State()) { Feature() } ``` - Changed: `SwitchStore` has gotten some quality-of-life improvements ([https://github.com/pointfreeco/swift-composable-architecture/pull/2029](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2029)). - `SwitchStore.init` can now take the initial enum state so that it can be switched over exhaustively. This initializer also relaxes certain compile-time constraints previously requiring only `CaseLet` views and an optional, trailing `Default` view. - `CaseLet` can now omit the `state` parameter label, making it more consistent with other APIs, like `Reducer.ifCaseLet`. - The older `SwitchStore` and `CaseLet` initializers have been soft-deprecated along with the `Default` view. ```swift // Before: SwitchStore(self.store) { CaseLet(state: /App.State.loggedIn, action: App.Action.loggedIn) { loggedInStore in LoggedInView(store: loggedInStore) } CaseLet(state: /App.State.loggedOut, action: App.Action.loggedOut) { loggedOutStore in LoggedOutView(store: loggedOutStore) } } // After: SwitchStore(self.store) { switch $0 { // Can now switch over initial state for exhaustivity at compile time case .loggedIn: CaseLet(/App.State.loggedIn, action: App.Action.loggedIn) { loggedInStore in LoggedInView(store: loggedInStore) } .buttonStyle(.plain) // Can now render arbitrary views/modifiers in the view builder case .loggedOut: CaseLet(/App.State.loggedOut, action: App.Action.loggedOut) { loggedOutStore in LoggedOutView(store: loggedOutStore) } } } ``` - Changed: `WithViewStore.debug` has been renamed to `WithViewStore._printChanges` for consistency with `Reducer._printChanges` ([https://github.com/pointfreeco/swift-composable-architecture/pull/2101](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2101)). - Fixed: `EffectTask.publisher` now properly escapes dependencies accessed within it ([https://github.com/pointfreeco/swift-composable-architecture/pull/1988](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1988)). - Fixed: `Reducer._printChanges()` is no longer disabled in tests ([https://github.com/pointfreeco/swift-composable-architecture/pull/1995](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1995)). This allows it to be used for debugging purposes during test runs. - Changed: The internal `Task.megaYield` tool, for more predictably testing concurrent code, is now configurable via the `TASK_MEGA_YIELD_COUNT` environment variable ([https://github.com/pointfreeco/swift-composable-architecture/pull/2064](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2064)). - Improved: The output format of `WithViewStore._printChanges()` has been improved ([https://github.com/pointfreeco/swift-composable-architecture/pull/1973](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1973)). - Improved: Runtime warnings will now emit XCTest failures in test code rather than in app code ([https://github.com/pointfreeco/swift-composable-architecture/pull/2059](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2059)). - Deprecated: Type-based cancel IDs have been deprecated ([https://github.com/pointfreeco/swift-composable-architecture/pull/2091](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2091)). Use hashable values, instead. - Deprecated: The actionless overload of `Store.scope(state:)` has been deprecated in favor of the `observe` parameter on view stores ([https://github.com/pointfreeco/swift-composable-architecture/pull/2097](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2097)). - Deprecated: `Effect.task` and `Effect.fireAndForget` have been soft-deprecated in favor of `Effect.run` ([https://github.com/pointfreeco/swift-composable-architecture/pull/2099](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2099)). - Infrastructure: Added test coverage for child/parent effect cancellation behavior ([https://github.com/pointfreeco/swift-composable-architecture/pull/1970](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1970)). - Infrastructure: Clean up effect cancellation logic ([https://github.com/pointfreeco/swift-composable-architecture/pull/1977](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1977)). - Infrastructure: Miscellaneous documentation/formatting fixes: Fixed missing `action` parameter in `ForEachStore` documentation (thanks [@​m-housh](https://togithub.com/m-housh), [https://github.com/pointfreeco/swift-composable-architecture/pull/1998](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1998)). Number fact tutorial fix (thanks [@​siliconsorcery](https://togithub.com/siliconsorcery), [https://github.com/pointfreeco/swift-composable-architecture/pull/1962](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1962)). `BindingAction` fix (thanks [@​Ryu0118](https://togithub.com/Ryu0118), [https://github.com/pointfreeco/swift-composable-architecture/pull/2019](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2019)). `withTaskCancellation(id:)` fix (thanks [@​bjford](https://togithub.com/bjford), [https://github.com/pointfreeco/swift-composable-architecture/pull/2049](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2049)). Formatting fix (thanks [@​mooyoung2309](https://togithub.com/mooyoung2309), [https://github.com/pointfreeco/swift-composable-architecture/pull/2056](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2056)). Update 'bindable state' to 'binding state' (thanks [@​Jager-yoo](https://togithub.com/Jager-yoo), [https://github.com/pointfreeco/swift-composable-architecture/pull/2054](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2054)). - Infrastructure: Added Russian README translation (thanks [@​artyom-ivanov](https://togithub.com/artyom-ivanov), [https://github.com/pointfreeco/swift-composable-architecture/pull/2014](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2014)). - Infrastructure: Added Polish README translation (thanks [@​MarcelStarczyk](https://togithub.com/MarcelStarczyk), [https://github.com/pointfreeco/swift-composable-architecture/pull/2040](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2040)). - Infrastructure: Bump dependencies. - Infrastructure: Bump Xcode demo project settings ([https://github.com/pointfreeco/swift-composable-architecture/pull/2042](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2042)). - Infrastructure: Clean up and test `TestStore.skipInFlightEffects` ([https://github.com/pointfreeco/swift-composable-architecture/pull/2057](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2057)). - Infrastructure: CI updates ([https://github.com/pointfreeco/swift-composable-architecture/pull/2060](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2060)). - Infrastructure: Document how exhaustive vs. non-exhaustive test stores work ([https://github.com/pointfreeco/swift-composable-architecture/pull/2096](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2096)). #### New Contributors - [@​m-housh](https://togithub.com/m-housh) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/1998](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1998) - [@​siliconsorcery](https://togithub.com/siliconsorcery) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/1962](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1962) - [@​artyom-ivanov](https://togithub.com/artyom-ivanov) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2014](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2014) - [@​Ryu0118](https://togithub.com/Ryu0118) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2019](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2019) - [@​MarcelStarczyk](https://togithub.com/MarcelStarczyk) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2040](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2040) - [@​mooyoung2309](https://togithub.com/mooyoung2309) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/2056](https://togithub.com/pointfreeco/swift-composable-architecture/pull/2056) **Full Changelog**: pointfreeco/swift-composable-architecture@0.52.0...0.53.0 ### [`v0.52.0`](https://togithub.com/pointfreeco/swift-composable-architecture/releases/tag/0.52.0) [Compare Source](https://togithub.com/pointfreeco/swift-composable-architecture/compare/0.51.0...0.52.0) #### What's Changed - Added: Support for `XCTModify` and non-exhaustive testing ([https://github.com/pointfreeco/swift-composable-architecture/pull/1939](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1939)). - Added: Library reducer operators are now annotated with `@warn_unqualified_access` to prevent accidental bugs ([https://github.com/pointfreeco/swift-composable-architecture/pull/1950](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1950)). - Added: `Effect.publisher` for bridging effects from Combine ([https://github.com/pointfreeco/swift-composable-architecture/pull/1958](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1958)). - Changed: `Effect<Action>.Send` has been renamed to `Send<Action>` (thanks [@​tgrapperon](https://togithub.com/tgrapperon), [https://github.com/pointfreeco/swift-composable-architecture/pull/1930](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1930)). - Changed: Dependencies have been bumped to their latest versions to encourage adoption of bug fixes ([https://github.com/pointfreeco/swift-composable-architecture/pull/1964](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1964)). - Fixed: Dependencies are no longer recursively propagated over effects ([https://github.com/pointfreeco/swift-composable-architecture/pull/1954](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1954)). - Fixed: `TestStore.init` now calls `prepareDependencies` in a `withDependencies` block ([https://github.com/pointfreeco/swift-composable-architecture/pull/1955](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1955)). - Infrastructure: Fix UI test for `ForEach` bindings ([https://github.com/pointfreeco/swift-composable-architecture/pull/1933](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1933)). - Infrastructure: Add missing documentation to `Store.init` (thanks [@​kristofferjohansson](https://togithub.com/kristofferjohansson), [https://github.com/pointfreeco/swift-composable-architecture/pull/1940](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1940)). - Infrastructure: DocC fixes ([https://github.com/pointfreeco/swift-composable-architecture/pull/1942](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1942), [https://github.com/pointfreeco/swift-composable-architecture/pull/1956](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1956)). - Infrastructure: Update latest version documentation link in README (thanks [@​yimajo](https://togithub.com/yimajo), [https://github.com/pointfreeco/swift-composable-architecture/pull/1943](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1943)) - Infrastructure: Fix `.forEach()` documentation (thanks [@​finestructure](https://togithub.com/finestructure), [https://github.com/pointfreeco/swift-composable-architecture/pull/1957](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1957)). - Infrastructure: Documentation grammar fixes (thanks [@​bjford](https://togithub.com/bjford), [https://github.com/pointfreeco/swift-composable-architecture/pull/1963](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1963)) #### New Contributors - [@​kristofferjohansson](https://togithub.com/kristofferjohansson) made their first contribution in [https://github.com/pointfreeco/swift-composable-architecture/pull/1940](https://togithub.com/pointfreeco/swift-composable-architecture/pull/1940) **Full Changelog**: pointfreeco/swift-composable-architecture@0.51.0...0.52.0 </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. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/cgrindel/rules_swift_package_manager). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xNDQuMiIsInVwZGF0ZWRJblZlciI6IjM1LjE0NC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
We ran into a race-condition where force unwrapping the cached value lead to a crash. I'd like to suggest switching to a safer if-var pattern as see in the
IfLetStoreimplementation to avoid potential crashes.