Skip to content

Commit 1da96de

Browse files
authored
Fix retain cycle regression from 1.21 (#3756)
1 parent 9dae1ff commit 1da96de

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

Sources/ComposableArchitecture/Store.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ public final class Store<State, Action>: _Store {
339339
.compactMap { [weak self] in (self?.currentState as? T)?._$id }
340340
.removeDuplicates()
341341
.dropFirst()
342-
.sink { [weak self] _ in
342+
.sink { [weak self, weak parent] _ in
343343
guard let scopeID = self?.scopeID
344344
else { return }
345345
parent?.removeChild(scopeID: scopeID)

Tests/ComposableArchitectureTests/StoreTests.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,69 @@ final class StoreTests: BaseTCATestCase {
13381338
#expect(store.count == 1729)
13391339
}
13401340
}
1341+
1342+
@Suite
1343+
struct ParentChildLifecycle {
1344+
@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
1345+
@MainActor
1346+
@Test
1347+
func parentChildLifecycle() async throws {
1348+
weak var parentStore: StoreOf<Parent>?
1349+
do {
1350+
let store = Store(initialState: Parent.State()) {
1351+
Parent()
1352+
}
1353+
parentStore = store
1354+
store.send(.presentButtonTapped)
1355+
guard let _ = store.scope(state: \.child, action: \.child) else {
1356+
Issue.record("Child is 'nil'")
1357+
return
1358+
}
1359+
}
1360+
#expect(parentStore == nil)
1361+
}
1362+
1363+
@Reducer struct Child {
1364+
@ObservableState struct State {
1365+
var count = 0
1366+
}
1367+
enum Action {
1368+
case incrementButtonTapped
1369+
}
1370+
var body: some Reducer<State, Action> {
1371+
Reduce { state, action in
1372+
switch action {
1373+
case .incrementButtonTapped:
1374+
state.count += 1
1375+
return .none
1376+
}
1377+
}
1378+
}
1379+
}
1380+
@Reducer struct Parent {
1381+
@ObservableState struct State {
1382+
@Presents var child: Child.State?
1383+
}
1384+
enum Action {
1385+
case child(PresentationAction<Child.Action>)
1386+
case presentButtonTapped
1387+
}
1388+
var body: some Reducer<State, Action> {
1389+
Reduce { state, action in
1390+
switch action {
1391+
case .child:
1392+
return .none
1393+
case .presentButtonTapped:
1394+
state.child = Child.State()
1395+
return .none
1396+
}
1397+
}
1398+
.ifLet(\.$child, action: \.child) {
1399+
Child()
1400+
}
1401+
}
1402+
}
1403+
}
13411404
}
13421405
#endif
13431406

0 commit comments

Comments
 (0)