Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Example/KnitExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SwiftUI

struct ContentView: View {

let resolver: Resolver
let resolver: BaseResolver

var body: some View {
VStack {
Expand All @@ -21,7 +21,7 @@ struct ContentView: View {
}

struct ContentView_Previews: PreviewProvider {
static let assembler = ScopedModuleAssembler<Resolver>([KnitExampleAssembly()])
static let assembler = ScopedModuleAssembler<BaseResolver>([KnitExampleAssembly()])
static var previews: some View {
return ContentView(resolver: assembler.resolver)
}
Expand Down
6 changes: 3 additions & 3 deletions Example/KnitExample/KnitExampleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import Knit
@main
struct KnitExampleApp: App {

let assembler: ScopedModuleAssembler<Resolver>
var resolver: Resolver { assembler.resolver }
let assembler: ScopedModuleAssembler<BaseResolver>
var resolver: BaseResolver { assembler.resolver }

@MainActor
init() {
assembler = ScopedModuleAssembler<Resolver>(
assembler = ScopedModuleAssembler<BaseResolver>(
[KnitExampleAssembly()]
)
}
Expand Down
2 changes: 1 addition & 1 deletion Example/KnitExample/KnitExampleAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import KnitMacros
// @knit internal
final class KnitExampleAssembly: ModuleAssembly {

typealias TargetResolver = Resolver
typealias TargetResolver = BaseResolver

static var dependencies: [any ModuleAssembly.Type] { [] }

Expand Down
2 changes: 1 addition & 1 deletion Example/KnitExample/KnitExampleUserAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Knit
/// An assembly expected to be registered at the user level rather than at the app level
final class KnitExampleUserAssembly: ModuleAssembly {

typealias TargetResolver = Resolver
typealias TargetResolver = BaseResolver

static var dependencies: [any ModuleAssembly.Type] { [] }

Expand Down
18 changes: 3 additions & 15 deletions Sources/Knit/Container.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,11 @@ import Swinject

The Knit.Container also performs the function of a weak wrapper of the `SwinjectContainer`.
*/
public class Container<TargetResolver>: Knit.Resolver {
public class Container<TargetResolver: Knit.Resolver> {

// MARK: - Knit.Resolver

public var resolver: TargetResolver {
self as! TargetResolver
}

/// Returns `true` if the backing container is still available in memory, otherwise `false`.
public var isAvailable: Bool {
_swinjectContainer != nil
}

// MARK: - SwinjectResolver

public func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> SwinjectResolver {
_unwrappedSwinjectContainer(file: file, function: function, line: line)
}
public let resolver: TargetResolver
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does keeping this property access mean all the existing Container.register methods remain the same?


// MARK: - Private Properties

Expand All @@ -39,6 +26,7 @@ public class Container<TargetResolver>: Knit.Resolver {
// This should not be promoted from `fileprivate` access level.
fileprivate init(_swinjectContainer: SwinjectContainer) {
self._swinjectContainer = _swinjectContainer
self.resolver = TargetResolver(_swinjectContainer: _swinjectContainer)
}
}

Expand Down
20 changes: 8 additions & 12 deletions Sources/Knit/Module/ModuleAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Swinject

public protocol ModuleAssembly<TargetResolver> {

associatedtype TargetResolver
associatedtype TargetResolver: Knit.Resolver

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

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

/// Filter the list of dependencies down to those which match the scope of this assembly
/// This can be overridden in apps with custom Resolver hierarchies
static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type]

/// Hints about this assembly using by DependencyBuilder. Designed for internal use
static var _assemblyFlags: [ModuleAssemblyFlags] { get }

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

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

static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type] {
return dependencies.filter {
// Default the scoped implementation to match types directly
return self.resolverType == $0.resolverType
}
}

static var _assemblyFlags: [ModuleAssemblyFlags] {
var result: [ModuleAssemblyFlags] = []
if self is any AutoInitModuleAssembly.Type {
Expand Down Expand Up @@ -84,6 +73,13 @@ public protocol GeneratedModuleAssembly: ModuleAssembly {
extension ModuleAssembly where Self: GeneratedModuleAssembly {
// Default the dependencies to using generatedDependencies scoped to those with compatible resolvers
public static var dependencies: [any ModuleAssembly.Type] { scoped(generatedDependencies) }

/// Filter the list of dependencies down to those which match the scope of this assembly
public static func scoped(_ dependencies: [any ModuleAssembly.Type]) -> [any ModuleAssembly.Type] {
return dependencies.filter { module in
return resolverType.inherits(from: module.resolverType)
}
}
}

/// Control the behavior of Assembly Overrides.
Expand Down
14 changes: 1 addition & 13 deletions Sources/Knit/Module/ScopedModuleAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
import Swinject

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

public let internalAssembler: ModuleAssembler

Expand Down Expand Up @@ -58,18 +58,6 @@ public final class ScopedModuleAssembler<TargetResolver> {
behaviors: [Behavior] = [],
postAssemble: ((Container<TargetResolver>) -> Void)? = nil
) throws {
// For provided modules, fail early if they are scoped incorrectly
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed since the TargetResolver check is already done at compile time.

for assembly in modules {
let moduleAssemblyType = type(of: assembly)
if moduleAssemblyType.resolverType != TargetResolver.self {
let scopingError = ScopedModuleAssemblerError.incorrectTargetResolver(
expected: String(describing: TargetResolver.self),
actual: String(describing: moduleAssemblyType.resolverType)
)

throw DependencyBuilderError.assemblyValidationFailure(moduleAssemblyType, reason: scopingError)
}
}
self.internalAssembler = try ModuleAssembler(
parent: parent,
_modules: modules,
Expand Down
54 changes: 54 additions & 0 deletions Sources/Knit/Resolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,58 @@ public protocol Resolver: AnyObject {

func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> SwinjectResolver

init(_swinjectContainer: SwinjectContainer)

/// Resolvers require a manual implementation that matches the inheritance structure of the Resolver
/// If ResolverB inherits from ResolverA then the ResolverB inherits function should match this
/// Example:
/// public func ResolverB: ResolverA {
/// static func inherits(from resolverType: Resolver.Type) -> Bool {
/// return self == resolverType || resolverType == ResolverA.self
/// }
/// }
static func inherits(from resolverType: Resolver.Type) -> Bool
Comment thread
skorulis-ap marked this conversation as resolved.

}

/// Default Resolver implementation. Designed to be inherited from
open class BaseResolver: Resolver {

private weak var _swinjectContainer: SwinjectContainer?

/// Returns `true` if the backing container is still available in memory, otherwise `false`.
public var isAvailable: Bool {
_swinjectContainer != nil
}

// MARK: - SwinjectResolver

public func unsafeResolver(file: StaticString, function: StaticString, line: UInt) -> SwinjectResolver {
_unwrappedSwinjectContainer(file: file, function: function, line: line)
}

public required init(_swinjectContainer: SwinjectContainer) {
self._swinjectContainer = _swinjectContainer
}

/// Default implementation uses pure equality
open class func inherits(from resolverType: Resolver.Type) -> Bool {
return self == resolverType
}

// Force unwraps the weak Container
func _unwrappedSwinjectContainer(
file: StaticString = #fileID,
function: StaticString = #function,
line: UInt = #line
) -> SwinjectContainer {
guard let _swinjectContainer else {
fatalError(
"\(function) incorrectly accessed the container for \(self) which has already been released",
file: file,
line: line
)
}
return _swinjectContainer
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extension Container {
name: makeUniqueCollectionRegistrationName(),
factory: { r in
MainActor.assumeIsolated {
let resolver = r.resolve(Container<TargetResolver>.self)! as! TargetResolver
let resolver = r.resolve(Container<TargetResolver>.self)!.resolver
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I must have missed this one previously

return factory(resolver)
}
}
Expand Down
Loading
Loading