Skip to content

Commit

Permalink
refactor: improve implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmikius committed Feb 2, 2024
1 parent 654c665 commit 9fa938c
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 72 deletions.
12 changes: 6 additions & 6 deletions Sources/DependencyInjection/Container.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import Foundation

/// A dependency injection container that manages the creation and resolution of services.
public final class Container: DIContainerProtocol {
public final class Container {
/// A shared instance of the container, accessible throughout the application.
public static var shared = Container()
/// A lock to ensure thread safety during registration and resolution.
Expand All @@ -41,7 +41,7 @@ public final class Container: DIContainerProtocol {
/// - factory: A closure that creates an instance of the service when needed.
public func register<T>(
_ instanceType: InstanceType = .transient,
to type: T.Type = T.self,
to type: T.Type,
factory: @escaping (Container) -> T
) {
lock.lock()
Expand All @@ -55,7 +55,7 @@ public final class Container: DIContainerProtocol {
/// - instanceType: The lifetime of the service instance (defaults to `transient`).
/// - type: The type of the service being registered (defaults to inferred type).
/// - value: An autoclosure that provides the service instance's value.
public func register<T>(_ instanceType: InstanceType = .transient, to type: T.Type = T.self, value: @escaping @autoclosure () -> T) {
public func register<T>(_ instanceType: InstanceType = .transient, to type: T.Type, value: @escaping @autoclosure () -> T) {
lock.lock()
defer { lock.unlock() }
let factory: (Container) -> T = { _ in value() }
Expand All @@ -66,7 +66,7 @@ public final class Container: DIContainerProtocol {
///
/// - Parameter type: The type of the service to resolve.
/// - Returns: The resolved service instance, or nil if not registered.
public func resolve<T>(_ type: T.Type = T.self) -> T? {
public func resolve<T>(_ type: T.Type) -> T? {
lock.lock()
defer { lock.unlock() }
let value: T? = storage[ServiceKey(type: type)]?.value(in: self)
Expand All @@ -79,8 +79,8 @@ public final class Container: DIContainerProtocol {
///
/// - Parameter type: The type of the service to resolve.
/// - Returns: The resolved service instance.
public func resolveRequired<T>(_ type: T.Type = T.self) -> T {
guard let unwrapped: T = resolve() else {
public func resolveRequired<T>(_ type: T.Type) -> T {
guard let unwrapped: T = resolve(type) else {
preconditionFailure("Unable to resolve service of type \(T.self). It is not registered.")
}
return unwrapped
Expand Down
50 changes: 0 additions & 50 deletions Sources/DependencyInjection/DIContainerProtocol.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/DependencyInjection/Inject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ public final class Inject<Service> {
public init() {}
/// Resolves the service from the specified container.
private func resolve(in container: Container) -> Service {
container.resolveRequired() // Obtain the service from the container
container.resolveRequired(Service.self) // Obtain the service from the container
}
}
11 changes: 3 additions & 8 deletions Sources/DependencyInjection/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,9 @@ internal final class Service {
/// - Parameter container: The container used for resolving dependencies (if needed).
/// - Returns: The resolved service instance as Any.
private func fetchValue(from container: Container) -> Any {
instance ?? create(in: container)
}

/// Creates a new instance of the service using the factory closure.
///
/// - Parameter container: The container used for resolving dependencies (if needed).
/// - Returns: The newly created service instance.
private func create(in container: Container) -> Any {
if let instance = instance {
return instance
}
let value = factory(container)
if type == .singleton { instance = value }
return value
Expand Down
2 changes: 1 addition & 1 deletion Sources/DependencyInjection/ServiceKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import Foundation

/// A key used for storing and retrieving services within the dependency injection container.
struct ServiceKey: Hashable {
internal struct ServiceKey: Hashable {
/// The type of the service associated with the key.
let type: Any.Type

Expand Down
11 changes: 6 additions & 5 deletions Tests/DependencyInjectionTests/ContainerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@ final class ContainerTests: XCTestCase {
container = .shared
}

func testRegisterFactoryAndResolveTransient() {
func testRegisterFactoryAndResolveTransient() throws {
container.register(.transient, to: MockService.self) { _ in
MockService()
}

let instance1 = container.resolve(MockService.self)
let instance2 = container.resolve(MockService.self)
let instance1 = try XCTUnwrap(container.resolve(MockService.self))
let instance2 = try XCTUnwrap(container.resolve(MockService.self))

XCTAssertNotEqual(instance1, instance2)
XCTAssertTrue(type(of: instance1) === type(of: instance2))
}

func testRegisterFactoryAndResolveSingleton() {
Expand All @@ -53,7 +54,7 @@ final class ContainerTests: XCTestCase {
let instance1 = container.resolve(MockService.self)
let instance2 = container.resolve(MockService.self)

XCTAssertEqual(instance1, instance2)
XCTAssertTrue(instance1 === instance2)
}

func testRegisterValueAndResolveTransient() {
Expand All @@ -79,7 +80,7 @@ final class ContainerTests: XCTestCase {
}
}

public class MockService: Equatable {
internal class MockService: Equatable {
var id = UUID()
public var didCallFoo = false
public var didCallBar = false
Expand Down
3 changes: 2 additions & 1 deletion Tests/DependencyInjectionTests/InjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class InjectTests: XCTestCase {
@Inject var service: MockService
@Inject var service1: MockService

XCTAssertEqual(service, service1)
XCTAssertTrue(service === service1)
}

func testInjectTransient() {
Expand All @@ -50,5 +50,6 @@ final class InjectTests: XCTestCase {
@Inject var service1: MockService

XCTAssertNotEqual(service, service1)
XCTAssertTrue(type(of: service) === type(of: service1))
}
}

0 comments on commit 9fa938c

Please sign in to comment.