Skip to content

Commit f1e35a1

Browse files
committed
Add preview option to shared container
1 parent c07cedb commit f1e35a1

File tree

4 files changed

+82
-16
lines changed

4 files changed

+82
-16
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Factory Changelog
22

3+
## 2.4.12
4+
5+
* Add preview convenience function to SharedContainer for SwiftUI Previews
6+
* New mechanism to enable StrictConcurrency in targets
7+
38
## 2.4.11
49

510
* Pulled evolution support due to SPM .unsafe flags problem - Issue #289

Sources/Factory/Factory/Containers.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626

2727
import Foundation
2828

29+
#if canImport(SwiftUI)
30+
import SwiftUI
31+
#endif
32+
2933
// MARK: - Container
3034

3135
/// This is the default Container provided for your convenience by Factory.
@@ -81,6 +85,34 @@ public protocol SharedContainer: ManagedContainer {
8185

8286
}
8387

88+
#if canImport(SwiftUI)
89+
/// Defines the default factory helpers for shared containers
90+
extension SharedContainer {
91+
/// Defines a preview convenience function to allow easy shared container transformation in SwiftUI Previews.
92+
/// ```swift
93+
/// #Preview {
94+
/// Container.preview {
95+
/// $0.requestUsers.register { MockAsyncRequest { User.mockUsers } }
96+
/// }
97+
/// MainView()
98+
/// }
99+
/// ```
100+
/// Without it you'd need something like...
101+
/// ```swift
102+
/// #Preview {
103+
/// let _ = Container.shared.with {
104+
/// $0.requestUsers.register { MockAsyncRequest { User.mockUsers } }
105+
/// }
106+
/// MainView()
107+
/// }
108+
/// ```
109+
public static func preview(_ transform: (Self) -> Void) -> EmptyView {
110+
transform(shared)
111+
return EmptyView()
112+
}
113+
}
114+
#endif
115+
84116
// MARK: - ManagedContainer
85117

86118
/// ManagedContainer defines the core protocol all Containers must adopt.

Sources/Factory/Factory/Key.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,23 @@ internal struct FactoryKey: Hashable {
5757
//
5858
// Obtaining and using the class name string directly on every call results in code that's 2-3x slower.
5959
private func globalIdentifier(for type: Any.Type) -> ObjectIdentifier {
60-
defer { globalVariableLock.unlock() }
61-
globalVariableLock.lock()
62-
let requestedTypeID = ObjectIdentifier(type)
63-
// if known return it
64-
if let knownID = globalKnownIdentifierTable[requestedTypeID] {
65-
return knownID
60+
globalVariableLock.withLock {
61+
let requestedTypeID = ObjectIdentifier(type)
62+
// if known return it
63+
if let knownID = globalKnownIdentifierTable[requestedTypeID] {
64+
return knownID
65+
}
66+
// this is what we're bypassing. extremely slow runtime function.
67+
let name = String(reflecting: type)
68+
// magic happens here, if name is already known then get original key for it
69+
let id = globalNameToIdentifierTable[name, default: requestedTypeID]
70+
// and save it so we don't have to do this again
71+
globalKnownIdentifierTable[requestedTypeID] = id
72+
#if DEBUG
73+
globalIdentifierToNameTable[id] = name
74+
#endif
75+
return id
6676
}
67-
// this is what we're bypassing. extremely slow runtime function.
68-
let name = String(reflecting: type)
69-
// magic happens here, if name is already known then get original key for it
70-
let id = globalNameToIdentifierTable[name, default: requestedTypeID]
71-
// and save it so we don't have to do this again
72-
globalKnownIdentifierTable[requestedTypeID] = id
73-
#if DEBUG
74-
globalIdentifierToNameTable[id] = name
75-
#endif
76-
return id
7777
}
7878

7979
// quickly denormalizes the requested type identifier to a known type identifier

Tests/FactoryTests/FactoryContainerTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import XCTest
22
@testable import Factory
33

4+
#if canImport(SwiftUI)
5+
import SwiftUI
6+
#endif
47

58
final class FactoryContainerTests: XCTestCase {
69

@@ -56,6 +59,32 @@ final class FactoryContainerTests: XCTestCase {
5659
XCTAssertTrue(service3.text() == "MyService")
5760
}
5861

62+
func testWithFunction() throws {
63+
let service1 = Container.shared.myServiceType()
64+
XCTAssertTrue(service1.text() == "MyService")
65+
66+
Container.shared.with {
67+
$0.myServiceType.register(factory: { MockService() })
68+
}
69+
70+
let service2 = Container.shared.myServiceType()
71+
XCTAssertTrue(service2.text() == "MockService")
72+
}
73+
74+
#if canImport(SwiftUI)
75+
func testPreviewFunction() throws {
76+
let service1 = Container.shared.myServiceType()
77+
XCTAssertTrue(service1.text() == "MyService")
78+
79+
let _ = Container.preview {
80+
$0.myServiceType.register(factory: { MockService() })
81+
}
82+
83+
let service2 = Container.shared.myServiceType()
84+
XCTAssertTrue(service2.text() == "MockService")
85+
}
86+
#endif
87+
5988
func testConvenienceFunctions() throws {
6089
XCTAssertNotNil(Container.shared.cachedCoverage())
6190
XCTAssertNotNil(Container.shared.graphCoverage())

0 commit comments

Comments
 (0)