Skip to content

Commit

Permalink
Merge pull request #216 from cashapp/skorulis/optional-abstract-regis…
Browse files Browse the repository at this point in the history
…tration

Create OptionalAbstractRegistration type
skorulis-ap authored Dec 5, 2024
2 parents ea39ba3 + b00dc4f commit b2c54c8
Showing 2 changed files with 55 additions and 0 deletions.
37 changes: 37 additions & 0 deletions Sources/Knit/Module/Container+AbstractRegistration.swift
Original file line number Diff line number Diff line change
@@ -18,6 +18,20 @@ extension Container {
abstractRegistrations().abstractRegistrations.append(registration)
}

/// Register that a service is expected to exist but no implementation is currently available
/// The concrete implementation must be registered or the dependency graph is considered invalid
/// - NOTE: We don't currently support abstract registrations with arguments
/// As this is an `Optional` Service type this allows special handling of the abstract registration for test environments:
/// If during testing and no concrete registration is available, then `nil` will be resolved automatically.
public func registerAbstract<Service>(
_ serviceType: Optional<Service>.Type,
name: String? = nil,
file: String = #fileID
) {
let registration = OptionalAbstractRegistration<Service>(name: name, file: file)
abstractRegistrations().abstractRegistrations.append(registration)
}

// Must be called before using `registerAbstract`
func registerAbstractContainer() -> AbstractRegistrationContainer {
let registrations = AbstractRegistrationContainer()
@@ -91,6 +105,29 @@ fileprivate struct RealAbstractRegistration<ServiceType>: AbstractRegistration {
}
}

/// An abstract registration for an optional service
fileprivate struct OptionalAbstractRegistration<ServiceType>: AbstractRegistration {
let name: String?
// Source file used for debugging. Not included in hash calculation or equality
let file: String

var serviceType: ServiceType.Type { ServiceType.self }

var key: RegistrationKey {
return .init(typeIdentifier: ObjectIdentifier(ServiceType.self), name: name)
}

func registerPlaceholder(
container: Container,
errorFormatter: ModuleAssemblerErrorFormatter,
dependencyTree: DependencyTree
) {
container.register(Optional<ServiceType>.self, name: name) { _ in
return nil
}
}
}

// MARK: - Inner types

extension Container {
18 changes: 18 additions & 0 deletions Tests/KnitTests/AbstractRegistrationTests.swift
Original file line number Diff line number Diff line change
@@ -73,6 +73,16 @@ final class AbstractRegistrationTests: XCTestCase {
)
}

@MainActor
func testOptionalAbstractRegistrations() {
let assembler = ModuleAssembler([Assembly3()])
let string = assembler.resolver.resolve(String?.self) ?? nil
XCTAssertNil(string)

let int = assembler.resolver.resolve(Optional<Int>.self) ?? nil
XCTAssertNil(int)
}

}

private struct Assembly1: AutoInitModuleAssembly {
@@ -86,3 +96,11 @@ private struct Assembly2: AutoInitModuleAssembly {
container.registerAbstract(String.self)
}
}

private struct Assembly3: AutoInitModuleAssembly {
static var dependencies: [any ModuleAssembly.Type] { [] }
func assemble(container: Container) {
container.registerAbstract(Optional<String>.self)
container.registerAbstract(Int?.self)
}
}

0 comments on commit b2c54c8

Please sign in to comment.