Skip to content

Commit 85f89f5

Browse files
authored
Don't cache preview app entry point dependencies (#321)
* Don't cache preview app entry point dependencies We have a longstanding gotcha where we tell folks to avoid instantiating their root model on their app entry point because any dependencies on the root model may negatively affect any preview in their application. This PR detects when a dependency is accessed in a SwiftUI preview app entry point and prevents it from influencing the cache using the call stack symbols available. While this isn't a silver bullet, and any async work kicked off from the app entry point could still affect things negatively, this should hopefully be mostly an improvement on the status quo and maybe we won't have to refer folks to this gotcha as often in the future. * wip * wip * Update AppEntryPoint.swift
1 parent 4a12189 commit 85f89f5

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

Sources/Dependencies/DependencyValues.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ public struct DependencyValues: Sendable {
268268
}
269269
set {
270270
if DependencyValues.isPreparing {
271+
if context == .preview, Thread.isPreviewAppEntryPoint {
272+
reportIssue("Ignoring dependencies prepared in preview app entry point")
273+
return
274+
}
271275
let cacheKey = CachedValues.CacheKey(id: TypeIdentifier(key), context: context)
272276
guard !cachedValues.cached.keys.contains(cacheKey) else {
273277
if cachedValues.cached[cacheKey]?.preparationID != DependencyValues.preparationID {
@@ -547,6 +551,9 @@ public final class CachedValues: @unchecked Sendable {
547551
case .live:
548552
value = (key as? any DependencyKey.Type)?.liveValue as? Key.Value
549553
case .preview:
554+
if Thread.isPreviewAppEntryPoint {
555+
return Key.previewValue
556+
}
550557
if !CachedValues.isAccessingCachedDependencies {
551558
value = CachedValues.$isAccessingCachedDependencies.withValue(true) {
552559
#if canImport(SwiftUI) && compiler(>=6)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Foundation
2+
3+
extension Thread {
4+
public static var isPreviewAppEntryPoint: Bool {
5+
guard ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
6+
else { return false }
7+
8+
var isPreviewAppEntryPoint = false
9+
for frame in callStackSymbols.reversed() {
10+
if !isPreviewAppEntryPoint, frame.containsSymbol("$s7SwiftUI3AppPAAE4mainyyFZ") {
11+
isPreviewAppEntryPoint = true
12+
} else if isPreviewAppEntryPoint,
13+
frame.containsSymbol("$s7SwiftUI6runAppys5NeverOxAA0D0RzlF")
14+
{
15+
return false
16+
}
17+
}
18+
return isPreviewAppEntryPoint
19+
}
20+
}
21+
22+
extension String {
23+
fileprivate func containsSymbol(_ symbol: String) -> Bool {
24+
utf8
25+
.reversed()
26+
.drop(while: { (48...57).contains($0) })
27+
.dropFirst(3)
28+
.starts(with: symbol.utf8.reversed())
29+
}
30+
}

0 commit comments

Comments
 (0)