Skip to content

Commit 2f66166

Browse files
author
Oguz Yuksel
committed
When PresentationState storage is copied (COW), clear effectNavigationIDPath.
This signals that presentation was moved to a new parent context. Detect this as parentContextChanged and recreate effect.
1 parent f8158b9 commit 2f66166

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

Sources/ComposableArchitecture/Reducer/Reducers/PresentationReducer.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public struct PresentationState<State> {
5757

5858
private var storage: Storage
5959
@usableFromInline var presentedID: NavigationIDPath?
60+
// Tracks navigation path when effect was created. Cleared on COW to detect parent context changes.
61+
@usableFromInline var effectNavigationIDPath: NavigationIDPath? = nil
6062

6163
public init(wrappedValue: State?) {
6264
self.storage = Storage(state: wrappedValue)
@@ -67,6 +69,7 @@ public struct PresentationState<State> {
6769
set {
6870
if !isKnownUniquelyReferenced(&self.storage) {
6971
self.storage = Storage(state: newValue)
72+
self.effectNavigationIDPath = nil // Clear on COW to trigger effect recreation
7073
} else {
7174
self.storage.state = newValue
7275
}
@@ -657,9 +660,16 @@ public struct _PresentationReducer<Base: Reducer, Destination: Reducer>: Reducer
657660
baseEffects = self.base.reduce(into: &state, action: action)
658661
}
659662

663+
let currentNavigationID = state[keyPath: self.toPresentationState].wrappedValue.map(self.navigationIDPath(for:))
664+
let storedEffectPath = initialPresentationState.effectNavigationIDPath
665+
666+
// Detect parent context change: if effectNavigationIDPath is nil but presentation exists,
667+
// it means COW occurred (effectNavigationIDPath was cleared in wrappedValue setter)
668+
let parentContextChanged = currentNavigationID != nil
669+
&& (storedEffectPath == nil || storedEffectPath != currentNavigationID)
670+
660671
let presentationIdentityChanged =
661-
initialPresentationState.presentedID
662-
!= state[keyPath: self.toPresentationState].wrappedValue.map(self.navigationIDPath(for:))
672+
initialPresentationState.presentedID != currentNavigationID
663673

664674
let dismissEffects: Effect<Base.Action>
665675
if presentationIdentityChanged,
@@ -676,14 +686,22 @@ public struct _PresentationReducer<Base: Reducer, Destination: Reducer>: Reducer
676686

677687
if presentationIdentityChanged, state[keyPath: self.toPresentationState].wrappedValue == nil {
678688
state[keyPath: self.toPresentationState].presentedID = nil
689+
state[keyPath: self.toPresentationState].effectNavigationIDPath = nil
679690
}
680691

681692
let presentEffects: Effect<Base.Action>
682-
if presentationIdentityChanged || state[keyPath: self.toPresentationState].presentedID == nil,
693+
// Recreate Empty effect if:
694+
// - Parent context changed (COW occurred during enum transition)
695+
// - Presentation identity changed (normal navigation)
696+
// - First time presenting (presentedID is nil)
697+
if (parentContextChanged
698+
|| presentationIdentityChanged
699+
|| state[keyPath: self.toPresentationState].presentedID == nil),
683700
let presentationState = state[keyPath: self.toPresentationState].wrappedValue,
684701
!isEphemeral(presentationState)
685702
{
686703
let presentationDestinationID = self.navigationIDPath(for: presentationState)
704+
state[keyPath: self.toPresentationState].effectNavigationIDPath = presentationDestinationID
687705
state[keyPath: self.toPresentationState].presentedID = presentationDestinationID
688706
presentEffects = .concatenate(
689707
.publisher { Empty(completeImmediately: false) }

0 commit comments

Comments
 (0)