Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ __Sections__

- Support for multiple arguments. Code clean up.
- Using parameter packs for multiple arguments
- Tests are converted to the new Swift Testing framework

## [1.0.4]

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ container.register { container in
In the factory closure, we typically just call the dependency initializer and we resolve its input parameters from the container. You can get rid of this duplicated boiler-plate by using `autoregister` method where you specify just the initializer that should be used to initialize the dependency, instead of writing the same factories over and over again. The above example then looks like this:
```swift
let container = Container()
container.autoregister(initializer: Dependency.init)
container.autoregister(in: .shared, initializer: Dependency.init)
```
Similarly, we can use autoregistration with an argument and replace this:
```swift
Expand Down Expand Up @@ -166,7 +166,7 @@ class Singletons {
static let container = Container()

static func configure() {
container.autoregister(initializer: Dependency.init)
container.autoregister(in: .shared, initializer: Dependency.init)
}
}

Expand All @@ -183,7 +183,7 @@ class Object {
When using the `@Injected` property wrapper, the dependency is resolved right in the moment when the property is instantiated. If you prefer to resolve the dependency only when it is accessed for the first time, you should rather use `@LazyInjected`:
```swift
let container = Container()
container.autoregister(initializer: Dependency.init)
container.autoregister(in: .shared, initializer: Dependency.init)

class Object {
@LazyInjected(from: container) var dependency: Dependency
Expand Down
6 changes: 6 additions & 0 deletions Sources/Container/Async/AsyncContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ public actor AsyncContainer: AsyncDependencyResolving, AsyncDependencyRegisterin
public func tryResolve<Dependency: Sendable, each Argument: Sendable>(type: Dependency.Type, arguments: repeat each Argument) async throws -> Dependency {
let identifier = RegistrationIdentifier(type: type, argumentTypes: repeat (each Argument).self)

if identifier.argumentCount > RegistrationIdentifierConstant.maximumArgumentCount {
throw ResolutionError.tooManyArguments(
message: "Maximum number of arguments is \(RegistrationIdentifierConstant.maximumArgumentCount). Got \(identifier.argumentCount)."
)
}

let registration = try getRegistration(with: identifier)

// Pack arguments into a tuple for storage - this matches how AsyncRegistration expects them
Expand Down
6 changes: 6 additions & 0 deletions Sources/Container/Sync/Container.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ open class Container: DependencyAutoregistering, DependencyResolving, Dependency
open func tryResolve<Dependency, each Argument>(type: Dependency.Type, arguments: repeat each Argument) throws -> Dependency {
let identifier = RegistrationIdentifier(type: type, argumentTypes: repeat (each Argument).self)

if identifier.argumentCount > RegistrationIdentifierConstant.maximumArgumentCount {
throw ResolutionError.tooManyArguments(
message: "Maximum number of arguments is \(RegistrationIdentifierConstant.maximumArgumentCount). Got \(identifier.argumentCount)."
)
}

let registration = try getRegistration(with: identifier)

// Pack arguments into a tuple for storage - this matches how Registration expects them
Expand Down
15 changes: 7 additions & 8 deletions Sources/Models/RegistrationIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import Foundation

/// Maximum number of arguments supported for dependency resolution
private enum Constant {
/// Maximum number of arguments supported for dependency resolution (enforced at resolve time)
enum RegistrationIdentifierConstant {
static let maximumArgumentCount = 3
}

Expand All @@ -17,7 +17,11 @@ struct RegistrationIdentifier: Sendable {
let typeIdentifier: ObjectIdentifier
let argumentIdentifiers: [ObjectIdentifier]

/// Initializer using parameter packs for any number of argument types
/// Number of argument types (used to enforce maximum at resolve time)
var argumentCount: Int { argumentIdentifiers.count }

/// Initializer using parameter packs for any number of argument types.
/// Registration with more than 3 arguments is allowed; resolution with more than 3 arguments will throw.
///
/// - Parameters:
/// - type: Type of the dependency
Expand All @@ -28,11 +32,6 @@ struct RegistrationIdentifier: Sendable {
var identifiers: [ObjectIdentifier] = []
repeat identifiers.append(ObjectIdentifier((each Argument).self))

precondition(
identifiers.count <= Constant.maximumArgumentCount,
"Maximum number of arguments is \(Constant.maximumArgumentCount). Got \(identifiers.count)."
)

argumentIdentifiers = identifiers
}

Expand Down
5 changes: 5 additions & 0 deletions Sources/Models/ResolutionError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ public enum ResolutionError: Error {
/// The dependency with the required type is registered within the container but the factory closure expects a different argument type
case unmatchingArgumentType(message: String)

/// More than the maximum supported number of arguments (3) was passed to resolve
case tooManyArguments(message: String)

public var localizedDescription: String {
switch self {
case let .dependencyNotRegistered(message):
return "Dependency not registered: \(message)"
case let .unmatchingArgumentType(message):
return "Unmatching argument type: \(message)"
case let .tooManyArguments(message):
return "Too many arguments: \(message)"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ public protocol AsyncDependencyRegistering {

// MARK: Overloaded factory methods
public extension AsyncDependencyRegistering {
/// Default ``DependencyScope`` value
///
/// The default value is `shared`
static var defaultScope: DependencyScope {
DependencyScope.shared
}

/// Register a dependency with an implicit type determined by the factory closure return type
///
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ public extension DependencyAutoregistering {
///
/// - Parameters:
/// - type: Type of the dependency to register. Default value is implicitly inferred from the initializer return type
/// - scope: Scope of the dependency. If `.new` is used, the initializer is called on each `resolve` call. If `.shared` is used, the initializer is called only the first time, the instance is cached and it is returned for all subsequent `resolve` calls, i.e. it is a singleton. The default value is `.shared`
/// - scope: Scope of the dependency. If `.new` is used, the initializer is called on each `resolve` call. If `.shared` is used, the initializer is called only the first time, the instance is cached and it is returned for all subsequent `resolve` calls, i.e. it is a singleton
/// - initializer: Initializer method of the `Dependency` that should be used to instantiate the dependency when it is being resolved from the container
func autoregister<Dependency, each Parameter>(
type: Dependency.Type = Dependency.self,
in scope: DependencyScope = Self.defaultScope,
in scope: DependencyScope,
initializer: @escaping (repeat each Parameter) -> Dependency
) {
let factory: Factory<Dependency> = { resolver in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ public protocol DependencyRegistering {

// MARK: Overloaded factory methods
public extension DependencyRegistering {
/// Default ``DependencyScope`` value
///
/// The default value is `shared`
static var defaultScope: DependencyScope {
DependencyScope.shared
}

/// Register a dependency with an implicit type determined by the factory closure return type
///
/// - Parameters:
Expand Down
25 changes: 0 additions & 25 deletions Tests/Common/Async/AsyncDITestCase.swift

This file was deleted.

15 changes: 15 additions & 0 deletions Tests/Common/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,21 @@ final class DependencyWithThreeArguments: Sendable {
}
}

/// Used only in tests to verify that more than 3 arguments throws ``ResolutionError/tooManyArguments(message:)``
final class DependencyWithFourArguments: Sendable {
let argument1: StructureDependency
let argument2: String
let argument3: Int
let argument4: Bool

init(argument1: StructureDependency, argument2: String, argument3: Int, argument4: Bool) {
self.argument1 = argument1
self.argument2 = argument2
self.argument3 = argument3
self.argument4 = argument4
}
}

final class DependencyWithAsyncInitWithTwoArguments: Sendable {
let argument1: StructureDependency
let argument2: String
Expand Down
25 changes: 0 additions & 25 deletions Tests/Common/Sync/DITestCase.swift

This file was deleted.

18 changes: 18 additions & 0 deletions Tests/Common/TestTags.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// TestTags.swift
// DependencyInjection
//
// Created on 19.12.2024.
//

import Testing

extension Tag {
@Tag static var async: Tag
@Tag static var sync: Tag
@Tag static var base: Tag
@Tag static var arguments: Tag
@Tag static var complex: Tag
@Tag static var autoregistration: Tag
@Tag static var propertyWrappers: Tag
}
Loading