Fix .onPreview/.onTest not working in Xcode previews#351
Open
bsudekum wants to merge 1 commit intohmlongco:mainfrom
Open
Fix .onPreview/.onTest not working in Xcode previews#351bsudekum wants to merge 1 commit intohmlongco:mainfrom
bsudekum wants to merge 1 commit intohmlongco:mainfrom
Conversation
Two issues prevented context factories from resolving in previews: 1. Registration and resolution of preview/test contexts were gated behind #if DEBUG. Since isPreview and isTest are runtime checks (environment variables), they don't need compile-time guards. Removed #if DEBUG for preview/test; kept it for debug context. 2. Xcode previews dynamically recompile and inject code, causing nominal types (TypedFactory<P,T>) to have different type identity across module boundaries. The as? TypedFactory<P,T> cast silently fails, so context factories are ignored and the default factory runs instead (often fatalError). Fix: Added untypedFactory property to AnyFactory that exposes the closure as Any. When the nominal struct cast fails, resolution falls back to casting the closure using structural function types (@sendable (P) -> T), which are identity-independent in Swift.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
.onPreviewand.onTestcontext factories silently fail in Xcode previews. The mock is registered and found, but never used — the default factory runs instead (oftenfatalError()), crashing the preview.Cause: Xcode previews dynamically recompile and inject code, creating duplicate
TypedFactory<P,T>types across module boundaries. Theas? TypedFactory<P,T>cast returnsnileven when both sides areTypedFactory<(), BillingUI>— same generic parameters, different runtime type identity.Fix
Add a structural function-type cast fallback. When the nominal
TypedFactory<P,T>cast fails, cast the underlying closure directly via@Sendable (P) -> T. Function types use structural typing inSwift, which is immune to the compilation-unit identity mismatch.
Registrations.swift—AnyFactoryexposesuntypedFactory: Any.resolve()falls back to casting the closure when the struct cast fails. Removed#if DEBUGfromisPreview/isTestinfactoryForCurrentContext().Resolver.swift— Same fallback for the global resolver path.Modifiers.swift— Removed#if DEBUGfrom context registrationdefault:case.The normal (non-preview) path is unchanged — the nominal cast succeeds first and the fallback is never reached.
Why remove
#if DEBUGfrom preview/test gatesisPreviewandisTestarefalsein release, so context factories never resolve outside debug builds. User code already wraps.onPreview/.onTestin#if DEBUG. The.onDebugcontext retains its#if DEBUGguard.