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
61 changes: 61 additions & 0 deletions Sources/Resolver/Resolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,42 @@ public final class Resolver {
fatalError("RESOLVER: '\(Service.self):\(name?.rawValue ?? "NONAME")' not resolved. To disambiguate optionals use resolver.optional().")
}

// Resolves and returns all named instances of the given Service type from the current registry or from its
/// parent registries.
///
/// - parameter type: Type of Services being resolved. Optional, may be inferred by assignment result type.
/// - parameter args: Optional arguments that may be passed to registration factory.
///
/// - returns: Instances of specified Service.
///
public static func resolveAll<Service>(_ type: Service.Type = Service.self, args: Any? = nil) -> [Service] {
lock.lock()
defer { lock.unlock() }
registrationCheck()
if let registrations = root.lookupAll(type) {
return registrations.compactMap { reg in return reg.resolve(resolver: root, args: args) }
}
return []
}

// Resolves and returns all named instances of the given Service type from the current registry or from its
/// parent registries.
///
/// - parameter type: Type of Services being resolved. Optional, may be inferred by assignment result type.
/// - parameter args: Optional arguments that may be passed to registration factory.
///
/// - returns: Instances of specified Service.
///
public final func resolveAll<Service>(_ type: Service.Type = Service.self, args: Any? = nil) -> [Service] {
lock.lock()
defer { lock.unlock() }
registrationCheck()
if let registrations = lookupAll(type) {
return registrations.compactMap { reg in return reg.scope.resolve(registration: reg, resolver: self, args: args) }
}
return []
}

/// Static function calls the root registry to resolve an optional Service type.
///
/// - parameter type: Type of Service being resolved. Optional, may be inferred by assignment result type.
Expand Down Expand Up @@ -319,6 +355,31 @@ public final class Resolver {
return nil
}

/// Internal function searches the current and child registries for all ResolverRegistration<Service>s that matches
/// the supplied type.
private final func lookupAll<Service>(_ type: Service.Type) -> [ResolverRegistration<Service>]? {
guard let values = lookupAllKeyed(type)?.values else {
return nil
}
return Array(values)
}

/// Internal function searches the current and child registries for all ResolverRegistration<Service>s that matches
/// the supplied type combind with their registered names.
private final func lookupAllKeyed<Service>(_ type: Service.Type) -> [String: ResolverRegistration<Service>]? {
let key = Int(bitPattern: ObjectIdentifier(type))
var result = namedRegistrations.filter { registration in
return registration.key.hasPrefix("\(key):")
}
for child in childContainers {
guard let childRegistrations = child.lookupAllKeyed(type) else {
continue
}
result.merge(childRegistrations, uniquingKeysWith: { parentEntry, _ in return parentEntry })
}
return result as? [String: ResolverRegistration<Service>]
}

/// Internal function adds a new registration to the proper container.
private final func add<Service>(registration: ResolverRegistration<Service>, with key: Int, name: Resolver.Name?) {
if let name = name?.rawValue {
Expand Down
66 changes: 66 additions & 0 deletions Tests/ResolverTests/ResolverNameTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,70 @@ class ResolverNameTests: XCTestCase {
XCTAssert(barney?.name == "Barney")
}

func testResolveAllOfType() {

resolver.register(name: .fred) { XYZNameService("Fred") }
resolver.register(name: .barney) { XYZNameService("Barney") }

let fredAndBarney: [XYZNameService] = resolver.resolveAll()

// Check all services resolved
XCTAssertEqual(fredAndBarney.count, 2)
var foundFred = false
var foundBarney = false
for service in fredAndBarney {
foundFred = foundFred || service.name == "Fred"
foundBarney = foundBarney || service.name == "Barney"
}

XCTAssertTrue(foundFred)
XCTAssertTrue(foundBarney)
}

func testResolveAllOfTypeInMultipleContainers() {

let childResolver = Resolver()
let parentResolver = Resolver(child: childResolver)

parentResolver.register(name: .fred) { XYZNameService("Fred") }
childResolver.register(name: .barney) { XYZNameService("Barney") }

let fredAndBarney: [XYZNameService] = parentResolver.resolveAll()

// Check all services resolved
XCTAssertEqual(fredAndBarney.count, 2)
var foundFred = false
var foundBarney = false
for service in fredAndBarney {
foundFred = foundFred || service.name == "Fred"
foundBarney = foundBarney || service.name == "Barney"
}

XCTAssertTrue(foundFred)
XCTAssertTrue(foundBarney)
}

func testResolveAllOfTypeInMultipleContainersWithOverride() {

let childResolver = Resolver()
let parentResolver = Resolver(child: childResolver)

parentResolver.register(name: .fred) { XYZEnhancedNameService("Fred") as XYZNameProtocol }
childResolver.register(name: .fred) { XYZNameService("Fred") as XYZNameProtocol }
parentResolver.register(name: .barney) { XYZNameService("Barney") as XYZNameProtocol }

let fredAndBarney: [XYZNameProtocol] = parentResolver.resolveAll()

// Check all services resolved
XCTAssertEqual(fredAndBarney.count, 2)
var foundFredFromParent = false
var foundBarney = false
for service in fredAndBarney {
foundFredFromParent = foundFredFromParent || (service.name == "Fred" && service is XYZEnhancedNameService)
foundBarney = foundBarney || service.name == "Barney"
}

XCTAssertTrue(foundFredFromParent)
XCTAssertTrue(foundBarney)
}
}
10 changes: 9 additions & 1 deletion Tests/ResolverTests/TestData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,21 @@ class XYZSessionService: XYZSessionProtocol {
var name: String = "XYZSessionService"
}

class XYZNameService {
protocol XYZNameProtocol {
var id: UUID { get }
var name: String { get set }
}

class XYZNameService: XYZNameProtocol {
let id = UUID()
var name: String
init(_ name: String) {
self.name = name
}
}
class XYZEnhancedNameService: XYZNameService {
let isEnhanced: Bool = true
}

class XYZNeverService {
}
Expand Down