Skip to content

Commit e06231c

Browse files
committed
Create a class based Knit Resolver
1 parent 69fb56d commit e06231c

11 files changed

+101
-462
lines changed

Sources/Knit/Container.swift

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,11 @@ import Swinject
1111

1212
The Knit.Container also performs the function of a weak wrapper of the `Swinject.Container`.
1313
*/
14-
public class Container<TargetResolver>: Knit.Resolver {
14+
public class Container<TargetResolver: Knit.Resolver> {
1515

1616
// MARK: - Knit.Resolver
1717

18-
public var resolver: TargetResolver {
19-
self as! TargetResolver
20-
}
21-
22-
/// Returns `true` if the backing container is still available in memory, otherwise `false`.
23-
public var isAvailable: Bool {
24-
_swinjectContainer != nil
25-
}
26-
27-
// MARK: - Swinject.Resolver
28-
29-
public func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> Swinject.Resolver {
30-
_unwrappedSwinjectContainer(file: file, function: function, line: line)
31-
}
18+
public let resolver: TargetResolver
3219

3320
// MARK: - Private Properties
3421

@@ -39,6 +26,7 @@ public class Container<TargetResolver>: Knit.Resolver {
3926
// This should not be promoted from `fileprivate` access level.
4027
fileprivate init(_swinjectContainer: Swinject.Container) {
4128
self._swinjectContainer = _swinjectContainer
29+
self.resolver = TargetResolver(_swinjectContainer: _swinjectContainer)
4230
}
4331
}
4432

Sources/Knit/Module/ModuleAssembly.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Swinject
77

88
public protocol ModuleAssembly<TargetResolver> {
99

10-
associatedtype TargetResolver
10+
associatedtype TargetResolver: Knit.Resolver
1111

1212
static var resolverType: Self.TargetResolver.Type { get }
1313

@@ -20,10 +20,6 @@ public protocol ModuleAssembly<TargetResolver> {
2020
/// A common case is a fake assembly that registers fake services matching those from the original module.
2121
static var replaces: [any ModuleAssembly.Type] { get }
2222

23-
/// Filter the list of dependencies down to those which match the scope of this assembly
24-
/// This can be overridden in apps with custom Resolver hierarchies
25-
static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type]
26-
2723
/// Hints about this assembly using by DependencyBuilder. Designed for internal use
2824
static var _assemblyFlags: [ModuleAssemblyFlags] { get }
2925

@@ -39,13 +35,6 @@ public extension ModuleAssembly {
3935

4036
static var replaces: [any ModuleAssembly.Type] { [] }
4137

42-
static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type] {
43-
return dependencies.filter {
44-
// Default the scoped implementation to match types directly
45-
return self.resolverType == $0.resolverType
46-
}
47-
}
48-
4938
static var _assemblyFlags: [ModuleAssemblyFlags] {
5039
var result: [ModuleAssemblyFlags] = []
5140
if self is any AutoInitModuleAssembly.Type {
@@ -84,6 +73,13 @@ public protocol GeneratedModuleAssembly: ModuleAssembly {
8473
extension ModuleAssembly where Self: GeneratedModuleAssembly {
8574
// Default the dependencies to using generatedDependencies scoped to those with compatible resolvers
8675
public static var dependencies: [any ModuleAssembly.Type] { scoped(generatedDependencies) }
76+
77+
/// Filter the list of dependencies down to those which match the scope of this assembly
78+
private static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type] {
79+
return dependencies.filter { module in
80+
return resolverType.inherits(from: module.resolverType)
81+
}
82+
}
8783
}
8884

8985
/// Control the behavior of Assembly Overrides.

Sources/Knit/Module/ScopedModuleAssembler.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Foundation
66
import Swinject
77

88
/// Module assembly which only allows registering assemblies which target a particular resolver type.
9-
public final class ScopedModuleAssembler<TargetResolver> {
9+
public final class ScopedModuleAssembler<TargetResolver: Knit.Resolver> {
1010

1111
public let internalAssembler: ModuleAssembler
1212

@@ -58,18 +58,6 @@ public final class ScopedModuleAssembler<TargetResolver> {
5858
behaviors: [Behavior] = [],
5959
postAssemble: ((Container<TargetResolver>) -> Void)? = nil
6060
) throws {
61-
// For provided modules, fail early if they are scoped incorrectly
62-
for assembly in modules {
63-
let moduleAssemblyType = type(of: assembly)
64-
if moduleAssemblyType.resolverType != TargetResolver.self {
65-
let scopingError = ScopedModuleAssemblerError.incorrectTargetResolver(
66-
expected: String(describing: TargetResolver.self),
67-
actual: String(describing: moduleAssemblyType.resolverType)
68-
)
69-
70-
throw DependencyBuilderError.assemblyValidationFailure(moduleAssemblyType, reason: scopingError)
71-
}
72-
}
7361
self.internalAssembler = try ModuleAssembler(
7462
parent: parent,
7563
_modules: modules,

Sources/Knit/Resolver.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,58 @@ public protocol Resolver: AnyObject {
1313

1414
func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> Swinject.Resolver
1515

16+
init(_swinjectContainer: Swinject.Container)
17+
18+
/// Resolvers require a manual implementation that matches the inheritance structure of the Resolver
19+
/// If ResolverB inherits from ResolverA then the ResolverB inherits function should match this
20+
/// Example:
21+
/// public func ResolverB: ResolverA {
22+
/// static func inherits(from resolverType: Resolver.Type) -> Bool {
23+
/// return self == resolverType || resolverType == ResolverA.self
24+
/// }
25+
/// }
26+
static func inherits(from resolverType: Resolver.Type) -> Bool
27+
28+
}
29+
30+
/// Default Resolver implementation. Designed to be inherited from
31+
open class BaseResolver: Resolver {
32+
33+
private weak var _swinjectContainer: Swinject.Container?
34+
35+
/// Returns `true` if the backing container is still available in memory, otherwise `false`.
36+
public var isAvailable: Bool {
37+
_swinjectContainer != nil
38+
}
39+
40+
// MARK: - Swinject.Resolver
41+
42+
public func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> Swinject.Resolver {
43+
_unwrappedSwinjectContainer(file: file, function: function, line: line)
44+
}
45+
46+
public required init(_swinjectContainer: Swinject.Container) {
47+
self._swinjectContainer = _swinjectContainer
48+
}
49+
50+
/// Default implementation uses pure equality
51+
open class func inherits(from resolverType: Resolver.Type) -> Bool {
52+
return self == resolverType
53+
}
54+
55+
// Force unwraps the weak Container
56+
func _unwrappedSwinjectContainer(
57+
file: StaticString = #fileID,
58+
function: StaticString = #function,
59+
line: UInt = #line
60+
) -> Swinject.Container {
61+
guard let _swinjectContainer else {
62+
fatalError(
63+
"\(function) incorrectly accessed the container for \(self) which has already been released",
64+
file: file,
65+
line: line
66+
)
67+
}
68+
return _swinjectContainer
69+
}
1670
}

Sources/Knit/ServiceCollection/Container+ServiceCollection.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension Container {
3030
name: makeUniqueCollectionRegistrationName(),
3131
factory: { r in
3232
MainActor.assumeIsolated {
33-
let resolver = r.resolve(Container<TargetResolver>.self)! as! TargetResolver
33+
let resolver = r.resolve(Container<TargetResolver>.self)!.resolver
3434
return factory(resolver)
3535
}
3636
}

0 commit comments

Comments
 (0)