diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1d316aa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# Mac OS X
+*.DS_Store
+
+# Xcode
+.build
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+*.xcuserstate
+project.xcworkspace/
+xcuserdata/
+Pods/*.xcodeproj/xcuserdata/
+xcuserdata/
+/.build
+/Packages
+
+# Generated files
+*.o
+*.pyc
+
+# Docs
+docs/docsets/XCoordinator.tgz
+docs/undocumented.json
+
+# Backup files
+*~.nib
+\#*#
+.#*
+
+.swiftpm
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0b43d04
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: swift
+os: osx
+osx_image: xcode10.1
+script:
+ - swift build
+ - swift test
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6fc0704
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 QuickBird Studios
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..3eec1be
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,22 @@
+// swift-tools-version:4.0
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+
+import PackageDescription
+
+let package = Package(
+ name: "XServiceLocator",
+ products: [
+ .library(
+ name: "XServiceLocator",
+ targets: ["XServiceLocator"]),
+ ],
+ targets: [
+ .target(
+ name: "XServiceLocator",
+ dependencies: []),
+ .testTarget(
+ name: "XServiceLocatorTests",
+ dependencies: ["XServiceLocator"]),
+ ]
+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f585063
--- /dev/null
+++ b/README.md
@@ -0,0 +1,255 @@
+# XServiceLocator
+
+*"Craving for proper Dependency Injection?"*
+
+
+
+
+
+# [![Build Status](https://travis-ci.com/quickbirdstudios/XServiceLocator.svg?branch=master)](https://travis-ci.com/quickbirdstudios/XServiceLocator) [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/XServiceLocator.svg)](https://cocoapods.org/pods/XServiceLocator) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/XServiceLocator) [![Platform](https://img.shields.io/badge/platform-iOS-lightgrey.svg)](https://github.com/quickbirdstudios/XServiceLocator) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/quickbirdstudios/XServiceLocator/blob/master/LICENSE)
+
+XServiceLocator is a light-weight Swift library/framework for dynamically providing objects with the dependencies they need. The library is based on the Service Locator pattern. The idea is that objects get their dependencies from a certain store. XServiceLocator enables you to use [seamless dependency injection](https://quickbirdstudios.com/blog/swift-dependency-injection-service-locators/) throughout your iOS app without the need for any background magic.
+
+## π© Components
+
+### π¦ Container
+
+Stores the configuration on how to create instances of the registered types
+
+### π οΈ Resolver
+
+Resolves the actual implementation for a type, by creating an instance of a class, using the configuration of the Container
+
+### π ServiceFactory
+
+A generic factory solution for creating instances of the generic type
+
+
+## πββοΈ Getting started
+
+### πΆββοΈ Basic steps
+
+1. Register all of the types using any of the two `register` methods with the container.
+
+
+```swift
+let container = Container()
+ .register(Int.self, instance: 10)
+ .register(Double.self) { _ in 20 }
+```
+
+2. Wherever you need an instance of the type, you can access it using any of the two `resolve` methods of the `Resolver`. Let's first get the Resolver from the `Container` and then use the resolver to resolve the dependencies.
+
+```swift
+ let resolver = container.resolver
+
+ let intValue = try! resolver.resolve(Int.self)
+ let doubleValue: Double = try! resolver.resolve()
+```
+
+### π² Custom Objects
+
+You can also register custom types or instances for protocols, such as:
+
+```swift
+// Setting up
+
+protocol Shape {
+ var name: String { get }
+}
+
+class Circle: Shape {
+ let name = "Circle"
+ let radius: Double
+
+ init(radius: Double) {
+ self.radius = radius
+ }
+
+}
+
+class Rectangle: Shape {
+ let name = "Rectangle"
+ let width: Double
+ let height: Double
+
+ init(width: Double, height: Double) {
+ self.width = width
+ self.height = height
+ }
+}
+
+struct Drawing {
+ let shape: Shape
+}
+
+// Registering all the dependencies
+
+let container = Container()
+ .register(Shape.self, instance: Circle(radius: 10))
+ .register(Circle.self) { _ in Circle(radius: 20) }
+ .register(Rectangle.self, instance: Rectangle(width: 30, height: 15))
+ .register(Drawing.self) { _ in
+ let shape = Rectangle(width: 10, height: 5)
+ return Drawing(shape: shape)
+ }
+
+
+// Resolving the dependencies
+
+let resolver = container.resolver
+
+let rectangle = try! resolver.resolve(Rectangle.self)
+let shape = try! resolver.resolve(Shape.self)
+let circle: Circle = try! resolver.resolve()
+let drawing: Drawing = try! resolver.resolve()
+
+// Accessing values
+
+print(rectangle.name) // Rectangle
+print(shape.name) // Circle
+print(circle.name) // Circle
+print(drawing.shape.name) // Rectangle
+
+print("\(rectangle.width), \(rectangle.height)") // 30.0, 15.0
+print((shape as! Circle).radius) // 10.0
+print(circle.radius) // 20.0
+```
+
+
+### π© Using already registered instances for subsequent registrations
+
+```swift
+// Registering all the dependencies
+let container = Container()
+ .register(Double.self, instance: 30)
+ .register(Rectangle.self, instance: Rectangle(width: 10, height: 20))
+ .register(Circle.self) { resolver in Circle(radius: try! resolver.resolve()) }
+
+
+// Resolving the dependencies
+
+let resolver = container.resolver
+
+let circle: Circle = try! resolver.resolve()
+
+
+// Accessing values
+
+circle.radius // 30.0
+```
+
+## πΆ Using (Child) Dependency Resolver
+
+A container can have another resolver as a dependency which can be used for resolution if the main resolver (container) fails to resolve the dependency.
+
+```swift
+// Registering all the dependencies
+
+let dependency = Container()
+ .register(Double.self, instance: 100)
+ .register(Shape.self, instance: Rectangle(width: 10, height: 20))
+
+let container = Container(dependency: dependency)
+ .register(Rectangle.self, instance: Rectangle(width: 15, height: 7.5))
+
+
+// Resolving the dependencies
+
+let resolver = container.resolver
+
+let shapeRectangle = try! resolver.resolve(Shape.self) as! Rectangle
+let rectangle: Rectangle = try! resolver.resolve()
+let doubleValue: Double = try! resolver.resolve()
+
+
+// Accessing values
+
+print("\(shapeRectangle.width), \(shapeRectangle.height)") // 10.0, 20.0
+print("\(rectangle.width), \(rectangle.height)") // 15.0, 7.5
+print(doubleValue) // 100
+```
+
+## πͺ Array of Resolvers
+
+An array of resolvers also act as a resolver. As soon as any element of the array is able to resolve successfully, the object is returned.
+
+```swift
+// Registering all the dependencies
+
+let container = Container()
+ .register(Int.self, instance: 10)
+ .register(Double.self, instance: 20)
+
+let container1 = Container()
+ .register(Float.self, instance: 30)
+ .register(Double.self, instance: 50)
+
+let arrayOfResolvers: Resolver = [
+ container,
+ container1,
+]
+
+
+// Resolving the dependencies
+let intValue = try! arrayOfResolvers.resolve(Int.self) // 10
+let floatValue = try! arrayOfResolvers.resolve(Float.self) // 30.0
+let doubleValue = try! arrayOfResolvers.resolve(Double.self) // 20.0
+```
+
+## βπ€·ββοΈ Why the ServiceLocator pattern?
+1. Loose coupling between classes/objects
+2. Provides better testability
+3. Provides extensibility. New instances can be easily registered and implementations can be switched without changing a lot.
+
+## βπ€ Why XServiceLocator?
+1. **Plug and Play**. Integrate the library and you are ready to go.
+2. **Supports Array of resolvers**. A combination of resolvers can be used for resolution of a type.
+3. Supports registration and resolution of any type.
+4. Developed and maintained by a group of developers who really care about the community and the quality of their solutions.
+5. Everything is built using Swift only. No Objective-C legacy code.
+
+## π Installation
+
+#### CocoaPods
+
+To integrate `XServiceLocator` into your Xcode project using CocoaPods, add this to your `Podfile`:
+
+```ruby
+pod 'XServiceLocator', '~> 1.0'
+```
+
+#### Carthage
+
+To integrate `XServiceLocator` into your Xcode project using Carthage, add this to your `Cartfile`:
+
+```
+github "quickbirdstudios/XServiceLocator" ~> 1.0
+```
+
+Then run `carthage update`.
+
+If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
+
+#### Swift Package Manager
+
+See [this WWDC presentation](https://developer.apple.com/videos/play/wwdc2019/408/) about more information how to adopt Swift packages in your app.
+
+Specify `https://github.com/quickbirdstudios/XServiceLocator.git` as the `XServiceLocator` package link.
+
+#### Manually
+
+If you prefer not to use any of the dependency managers, you can integrate `XServiceLocator` into your iOS project manually, by downloading the source code and placing the files in your project directory.
+
+## π€ Author
+This framework is created with β€οΈ by [QuickBird Studios](https://quickbirdstudios.com).
+
+## β€οΈ Contributing
+
+Open an issue if you need help, if you found a bug, or if you want to discuss a feature request.
+
+Open a PR if you want to make changes to XServiceLocator.
+
+## π License
+
+XServiceLocator is released under an MIT license. See [License.md](https://github.com/quickbirdstudios/XServiceLocator/blob/master/LICENSE) for more information.
diff --git a/Sources/XServiceLocator/Container.swift b/Sources/XServiceLocator/Container.swift
new file mode 100644
index 0000000..2b60c91
--- /dev/null
+++ b/Sources/XServiceLocator/Container.swift
@@ -0,0 +1,101 @@
+//
+// Container.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+/// Stores the configuration on how to create instances of the registered types
+public class Container {
+ let dependency: Resolver?
+ let factories: [AnyServiceFactory]
+
+ /// Initializes a new container with an optional child resolver
+ /// - Parameter dependency: a child resolver that is used when the dependency cannot be resolved by this container.
+ /// However, the container first tries to resolve the dependencies by itself
+ public init(dependency: Resolver? = nil) {
+ self.dependency = dependency
+ self.factories = []
+ }
+
+ /// Initializes a new container with an optional child resolver and an array of factories
+ /// - Parameter dependency: a child resolver that is used when the dependency cannot be resolved by this container.
+ /// However, the container first tries to resolve the dependencies by itself
+ /// - Parameter factories: an array of already registered types.
+ init(dependency: Resolver? = nil, factories: [AnyServiceFactory]) {
+ self.dependency = dependency
+ self.factories = factories
+ }
+
+ // MARK: - Register
+
+ /// Register the `instance` as a object of Type `ServiceType`. The same instance will be resolved on every resolve call.
+ /// - Parameter interface: The target type `ServiceType` to register the object as
+ /// - Parameter instance: The instance to register
+ public func register(_ interface: ServiceType.Type, instance: ServiceType) -> Container {
+ return register(interface) { _ in instance }
+ }
+
+ /// Register the `factory` as a template to create object of Type `ServiceType`. The `factory` will be called on
+ /// every resolve call which means that a new instance will be created on every resolve call.
+ /// - Parameter interface: The target type `ServiceType` to register the object as
+ /// - Parameter factory: The factory/closure that is used to create the instance of type `ServiceType`
+ public func register(_ interface: ServiceType.Type,
+ _ factory: @escaping (Resolver) -> ServiceType) -> Container {
+
+ let newFactory = BasicServiceFactory(interface, factory: { resolver in
+ factory(resolver)
+ })
+ return .init(dependency: dependency, factories: factories + [AnyServiceFactory(newFactory)])
+ }
+}
+
+// MARK: - Resolver
+
+extension Container: Resolver {
+ /// returns a resolver that can be used to resolve the container objects
+ public var resolver: Resolver { return self as Resolver }
+
+ /// Resolves to an instance of type `ServiceType` and throws if no instance/factory has already been registered.
+ /// In case the container is not able to resolve the instance type, it will try to resolve it using its dependency.
+ /// An `Error.factoryNotFound` will be thrown in case the resolution is not possible.
+ public func resolve(_ type: ServiceType.Type) throws -> ServiceType {
+ guard let factory = factories.first(where: { $0.supports(type) }) else {
+ guard let resolvedByDependency = try dependency?.resolve(type) else {
+ throw Container.unableToResolve(type)
+ }
+
+ return resolvedByDependency
+ }
+
+ return try factory.resolve(self)
+ }
+
+ /// Resolves to an instance of type `ServiceType` and throws if no instance/factory has already been registered.
+ /// In case the container is not able to resolve the instance type, it will try to resolve it using its dependency.
+ /// An `Error.factoryNotFound` will be thrown in case the resolution is not possible.
+ public func resolve() throws -> ServiceType {
+ return try resolve(ServiceType.self)
+ }
+}
+
+// MARK: - Error
+
+extension Container {
+ public static func unableToResolve(_ type: ServiceType.Type) -> Error {
+ return .factoryNotFound(service: type)
+ }
+
+ public enum Error: Swift.Error, Equatable {
+ public static func == (lhs: Container.Error, rhs: Container.Error) -> Bool {
+ switch (lhs, rhs) {
+ case let (.factoryNotFound(lhsType), .factoryNotFound(rhsType)):
+ return lhsType == rhsType
+ }
+ }
+
+ case factoryNotFound(service: Any.Type)
+ }
+}
diff --git a/Sources/XServiceLocator/Extensions/Array+Resolver.swift b/Sources/XServiceLocator/Extensions/Array+Resolver.swift
new file mode 100644
index 0000000..808231c
--- /dev/null
+++ b/Sources/XServiceLocator/Extensions/Array+Resolver.swift
@@ -0,0 +1,25 @@
+//
+// Array+Resolver.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+extension Array: Resolver where Element == Resolver {
+
+ /// tries to resolve to an instance of `ServiceType` and returns the instance as soon as the first element(resolver)
+ /// resolves it successfully.
+ public func resolve(_ type: ServiceType.Type) throws -> ServiceType {
+ guard !isEmpty else { throw EmptyResolverError() }
+
+ return try firstToResolve({ try $0.resolve(type) })
+ }
+
+ public func resolve() throws -> ServiceType {
+ return try resolve(ServiceType.self)
+ }
+
+ struct EmptyResolverError: Error { }
+}
diff --git a/Sources/XServiceLocator/Extensions/Collection+FirstToResolve.swift b/Sources/XServiceLocator/Extensions/Collection+FirstToResolve.swift
new file mode 100644
index 0000000..f212c48
--- /dev/null
+++ b/Sources/XServiceLocator/Extensions/Collection+FirstToResolve.swift
@@ -0,0 +1,22 @@
+//
+// Collection+FirstToResolve.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+// MARK: Internal API
+
+extension Collection where Index == Int {
+
+ /// tries to resolve the `ServiceType` and returns the instance as soon as the first element resolves it successfully.
+ func firstToResolve(_ factory: (Element) throws -> ServiceType) throws -> ServiceType {
+ for element in self {
+ if let resolved = try? factory(element) { return resolved }
+ }
+
+ throw Container.unableToResolve(ServiceType.self)
+ }
+}
diff --git a/Sources/XServiceLocator/Resolver.swift b/Sources/XServiceLocator/Resolver.swift
new file mode 100644
index 0000000..8b6b0c4
--- /dev/null
+++ b/Sources/XServiceLocator/Resolver.swift
@@ -0,0 +1,17 @@
+//
+// Resolver.swift
+// X
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+public protocol Resolver {
+ /// Resolves to an instance of type `ServiceType` and throws if no instance/factory has already been registered.
+ func resolve(_ type: ServiceType.Type) throws -> ServiceType
+
+ /// makes use of type inference to resolve to an instance of type `ServiceType` and throws if no instance/factory
+ /// has already been registered. Makes use o
+ func resolve() throws -> ServiceType
+}
diff --git a/Sources/XServiceLocator/ServiceFactory/AnyServiceFactory.swift b/Sources/XServiceLocator/ServiceFactory/AnyServiceFactory.swift
new file mode 100644
index 0000000..9cf1219
--- /dev/null
+++ b/Sources/XServiceLocator/ServiceFactory/AnyServiceFactory.swift
@@ -0,0 +1,31 @@
+//
+// AnyServiceFactory.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+/// Type-erased implementation of `ServiceFactory`
+final class AnyServiceFactory {
+ private let _resolve: (Resolver) throws -> Any
+ private let _supports: (Any.Type) -> Bool
+
+ init(_ serviceFactory: T) {
+
+ self._resolve = { resolver -> Any in
+ try serviceFactory.resolve(resolver)
+ }
+
+ self._supports = { $0 == T.ServiceType.self }
+ }
+
+ func resolve(_ resolver: Resolver) throws -> ServiceType {
+ return try _resolve(resolver) as! ServiceType
+ }
+
+ func supports(_ serviceType: ServiceType.Type) -> Bool {
+ return _supports(serviceType)
+ }
+}
diff --git a/Sources/XServiceLocator/ServiceFactory/BasicServiceFactory.swift b/Sources/XServiceLocator/ServiceFactory/BasicServiceFactory.swift
new file mode 100644
index 0000000..905e38b
--- /dev/null
+++ b/Sources/XServiceLocator/ServiceFactory/BasicServiceFactory.swift
@@ -0,0 +1,25 @@
+//
+// BasicServiceFactory.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+/// Implementation of `ServiceFactory` that is used to generate instances of the generic type `ServiceType`
+struct BasicServiceFactory: ServiceFactory {
+ private let factory: (Resolver) throws -> ServiceType
+
+ /// initializes the factory with type `ServiceType` and a factory for creation of the type
+ /// - Parameter type: The type of the object that this factory supports/creates
+ /// - Parameter factory: The factory method that takes a resolver and should return an instance of the type
+ init(_ type: ServiceType.Type, factory: @escaping (Resolver) throws -> ServiceType) {
+ self.factory = factory
+ }
+
+ /// tries resolving/generating the instance of generic type using the passed `Resolver`
+ func resolve(_ resolver: Resolver) throws -> ServiceType {
+ return try factory(resolver)
+ }
+}
diff --git a/Sources/XServiceLocator/ServiceFactory/ServiceFactory.swift b/Sources/XServiceLocator/ServiceFactory/ServiceFactory.swift
new file mode 100644
index 0000000..f27d017
--- /dev/null
+++ b/Sources/XServiceLocator/ServiceFactory/ServiceFactory.swift
@@ -0,0 +1,23 @@
+//
+// ServiceFactory.swift
+// XServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+/// Generic factory solution that is used to resolve/create instances of type `ServiceType`
+protocol ServiceFactory {
+ associatedtype ServiceType
+
+ /// tries resolving/generating the instance of generic type using the passed `Resolver`
+ func resolve(_ resolver: Resolver) throws -> ServiceType
+}
+
+extension ServiceFactory {
+ /// Checks if the service factory supports creation of the specific `ServiceType`
+ func supports(_ type: ServiceType.Type) -> Bool {
+ return type == ServiceType.self
+ }
+}
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
new file mode 100644
index 0000000..db53c7f
--- /dev/null
+++ b/Tests/LinuxMain.swift
@@ -0,0 +1,7 @@
+import XCTest
+
+import XServiceLocatorTests
+
+var tests = [XCTestCaseEntry]()
+tests += XServiceLocator.allTests()
+XCTMain(tests)
diff --git a/Tests/XServiceLocatorTests/XServiceLocatorTests.swift b/Tests/XServiceLocatorTests/XServiceLocatorTests.swift
new file mode 100644
index 0000000..d9f646e
--- /dev/null
+++ b/Tests/XServiceLocatorTests/XServiceLocatorTests.swift
@@ -0,0 +1,190 @@
+//
+// XServiceLocatorTests.swift
+// XServiceLocatorTests
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+@testable import XServiceLocator
+import XCTest
+
+class XServiceLocatorTests: XCTestCase {
+
+ func test_registerInstance_resolvesCorrectly() {
+ let resolver = Container()
+ .register(TestProtocol.self, instance: TestClass(id: 1, score: .random(in: 0...10)))
+ let instance: TestProtocol = try! resolver.resolve()
+
+ XCTAssert(instance is TestClass)
+ }
+
+ func test_registerFactory_resolvesCorrectly() {
+ let resolver = Container()
+ .register(TestProtocol.self) { _ in TestClass(id: 1, score: .random(in: 0...10)) }
+ let instance: TestProtocol = try! resolver.resolve()
+
+ XCTAssert(instance is TestClass)
+ }
+
+ func test_registerInstance_resolvesToSameInstanceEveryTime() {
+ let resolver = Container()
+ .register(TestClass.self, instance: TestClass(id: 1, score: 2))
+
+ let instance1: TestClass = try! resolver.resolve()
+ let instance2: TestClass = try! resolver.resolve()
+
+ XCTAssertTrue(instance1 === instance2)
+ }
+
+ func test_registerFactory_resolvesToNewInstanceEveryTime() {
+ let resolver = Container()
+ .register(TestClass.self) { _ in TestClass(id: 5, score: 10) }
+
+ let instance1: TestClass = try! resolver.resolve()
+ let instance2: TestClass = try! resolver.resolve()
+
+ XCTAssertFalse(instance1 === instance2)
+ }
+
+ func test_registerProtocolAndClass_resolvesCorrectly() {
+ let resolver = Container()
+ .register(TestProtocol.self, instance: TestClass(id: 1, score: 10))
+ .register(TestClass.self, instance: TestClass(id: 2, score: 20))
+
+ let resolvedProtocol: TestProtocol = try! resolver.resolve()
+ let resolvedInstance: TestClass = try! resolver.resolve()
+
+ XCTAssert(resolvedProtocol is TestClass)
+
+ XCTAssertEqual(resolvedProtocol.id, 1)
+ XCTAssertEqual((resolvedProtocol as! TestClass).score, 10)
+
+ XCTAssertEqual(resolvedInstance.id, 2)
+ XCTAssertEqual(resolvedInstance.score, 20)
+ }
+
+ func test_registerSingleInstance_resolvesCorrectly() {
+ let expectedInstance = TestClass(id: 1, score: 10)
+ let resolver = Container().register(TestProtocol.self, instance: expectedInstance)
+ let instance: TestProtocol = try! resolver.resolve()
+
+ guard let castedInstance = instance as? TestClass else {
+ XCTFail("Expected Instance to be of type `\(String(describing: TestClass.self))`")
+ return
+ }
+
+ XCTAssert(castedInstance === expectedInstance,
+ "Expected resolved instance and original instance to be the exact same object")
+ }
+
+ func test_nonregisteredInstance_factoryNotFound() throws {
+ let resolver = Container().register(TestProtocol.self, instance: TestStruct(id: 1))
+
+ XCTAssertThrowsError(try resolver.resolve(DummyProtocol.self)) { error in
+ XCTAssertEqual(error as? Container.Error, Container.Error.factoryNotFound(service: DummyProtocol.self))
+ }
+ }
+
+ func test_registerTwoImplementations_lastOneOverridesPreviousOne() {
+ let first = TestStruct(id: 1)
+ let second = TestStruct(id: 2)
+
+ let resolver = Container().register(TestProtocol.self, instance: first)
+ .register(TestProtocol.self, instance: second)
+
+ let resolved: TestProtocol = try! resolver.resolve()
+
+ XCTAssertEqual(resolved.id, first.id)
+ XCTAssertNotEqual(resolved.id, second.id)
+ }
+
+ func test_alreadyRegisteredDependencies_canBeResolvedForSubsequenetRegistrations() {
+ let container = Container()
+ .register(Int.self, instance: 100)
+ .register(TestProtocol.self) { resolver in TestStruct(id: try! resolver.resolve(Int.self)) }
+
+ let resolvedInt = try! container.resolve(Int.self)
+ let resolvedObject = try! container.resolve(TestProtocol.self)
+
+ XCTAssertEqual(resolvedInt, resolvedObject.id)
+ }
+
+ func test_registerInDependency_canBeResolvedByParent() {
+ let expectedId = 100
+ let first = Container().register(Int.self, instance: expectedId)
+ let second = Container(dependency: first)
+ .register(TestProtocol.self) { resolver in TestStruct(id: try! resolver.resolve(Int.self)) }
+
+ let resolvedIntByFirst = try! first.resolve(Int.self)
+ let resolvedIntBySecond = try! second.resolve(Int.self)
+
+ XCTAssertEqual(resolvedIntByFirst, resolvedIntBySecond)
+
+ let resolved = try! second.resolve(TestProtocol.self)
+ XCTAssertEqual(resolved.id, expectedId)
+ }
+
+ func test_registerInDependency_isOverridenByParentWhenAccessViaParent() {
+ let first = Container().register(Int.self, instance: 100)
+ let second = Container(dependency: first)
+ .register(TestProtocol.self) { resolver in TestStruct(id: 150) }
+ .register(Int.self, instance: 200)
+
+ let resolvedIntByFirst = try! first.resolve(Int.self)
+ let resolvedIntBySecond = try! second.resolve(Int.self)
+
+ XCTAssertNotEqual(resolvedIntByFirst, resolvedIntBySecond)
+ XCTAssertEqual(resolvedIntByFirst, 100)
+ XCTAssertEqual(resolvedIntBySecond, 200)
+ }
+
+ func test_ArrayOfResolvers_resolvesAll() {
+ let resolver: Resolver = [
+ Container().register(Int.self, instance: 100),
+ Container().register(Float.self, instance: 200),
+ Container().register(Double.self, instance: 300),
+ ]
+
+ XCTAssertEqual(try! resolver.resolve(Int.self), 100)
+ XCTAssertEqual(try! resolver.resolve(Float.self), 200)
+ XCTAssertEqual(try! resolver.resolve(Double.self), 300)
+ }
+
+ func test_arrayOfResolvers_resolvesTheFirstMatchInTheArray() {
+ let first = Container().register(Int.self, instance: 100)
+ let second = Container().register(Int.self, instance: 200)
+ let third = Container().register(Int.self, instance: 300)
+
+ let resolver1: Resolver = [first, second]
+ let resolver2: Resolver = [first, third]
+ let resolver3: Resolver = [first, second, third]
+
+ XCTAssertEqual(try! resolver1.resolve(Int.self), 100)
+ XCTAssertEqual(try! resolver2.resolve(Int.self), 100)
+ XCTAssertEqual(try! resolver3.resolve(Int.self), 100)
+
+ let resolver4: Resolver = [second, third]
+ XCTAssertEqual(try! resolver4.resolve(Int.self), 200)
+
+ let resolver5: Resolver = [first, second, third].reversed()
+ XCTAssertEqual(try! resolver5.resolve(Int.self), 300)
+ }
+
+ func test_emptyArrayOfResolvers_emptyResolverError() {
+ let resolver: Resolver = []
+
+ XCTAssertThrowsError(try resolver.resolve(Int.self)) { error in
+ XCTAssert(error is Array.EmptyResolverError)
+ }
+ }
+
+ func test_arrayOfResolvers_factoryNotFound() {
+ let resolver: Resolver = [
+ Container().register(Double.self, instance: 100)
+ ]
+
+ XCTAssertThrowsError(try resolver.resolve(Int.self)) { error in
+ XCTAssertEqual(error as? Container.Error, Container.Error.factoryNotFound(service: Int.self))
+ }
+ }
+}
diff --git a/Tests/XServiceLocatorTests/utils/DummyProtocol.swift b/Tests/XServiceLocatorTests/utils/DummyProtocol.swift
new file mode 100644
index 0000000..b611857
--- /dev/null
+++ b/Tests/XServiceLocatorTests/utils/DummyProtocol.swift
@@ -0,0 +1,11 @@
+//
+// DummyProtocol.swift
+// XServiceLocatorTests
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+protocol DummyProtocol {
+}
diff --git a/Tests/XServiceLocatorTests/utils/TestClass.swift b/Tests/XServiceLocatorTests/utils/TestClass.swift
new file mode 100644
index 0000000..1f63212
--- /dev/null
+++ b/Tests/XServiceLocatorTests/utils/TestClass.swift
@@ -0,0 +1,18 @@
+//
+// TestClass.swift
+// XServiceLocatorTests
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+class TestClass: TestProtocol {
+ let id: Int
+ let score: Double
+
+ init(id: Int, score: Double) {
+ self.id = id
+ self.score = score
+ }
+}
diff --git a/Tests/XServiceLocatorTests/utils/TestProtocol.swift b/Tests/XServiceLocatorTests/utils/TestProtocol.swift
new file mode 100644
index 0000000..90ef3f8
--- /dev/null
+++ b/Tests/XServiceLocatorTests/utils/TestProtocol.swift
@@ -0,0 +1,12 @@
+//
+// TestProtocol.swift
+// XServiceLocatorTests
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+public protocol TestProtocol {
+ var id: Int { get }
+}
diff --git a/Tests/XServiceLocatorTests/utils/TestStruct.swift b/Tests/XServiceLocatorTests/utils/TestStruct.swift
new file mode 100644
index 0000000..0e1a9c9
--- /dev/null
+++ b/Tests/XServiceLocatorTests/utils/TestStruct.swift
@@ -0,0 +1,12 @@
+//
+// TestStruct.swift
+// XServiceLocatorTests
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+import Foundation
+
+struct TestStruct: TestProtocol, Equatable {
+ let id: Int
+}
diff --git a/XServiceLocator.jpeg b/XServiceLocator.jpeg
new file mode 100644
index 0000000..1d977ba
Binary files /dev/null and b/XServiceLocator.jpeg differ
diff --git a/XServiceLocator.podspec b/XServiceLocator.podspec
new file mode 100644
index 0000000..bb64c31
--- /dev/null
+++ b/XServiceLocator.podspec
@@ -0,0 +1,14 @@
+Pod::Spec.new do |spec|
+ spec.name = 'XServiceLocator'
+ spec.homepage = 'https://github.com/quickbirdstudios/XServiceLocator'
+ spec.version = '1.0.0'
+ spec.summary = 'XServiceLocator is a light-weight library for dynamically providing objects with the dependencies they need.'
+ spec.license = { :type => 'MIT' }
+ spec.author = { 'Stefan Kofler' => 'stefan.kofler@quickbirdstudios.com', 'Mathias Quintero' => 'mathias.quintero@quickbirdstudios.com', 'Ghulam Nasir' => 'ghulam.nasir@quickbirdstudios.com' }
+ spec.source = { :git => 'https://github.com/quickbirdstudios/xservicelocator.git', :tag => spec.version }
+ spec.source_files = 'Sources/XServiceLocator/**/*.swift'
+ spec.framework = 'Foundation'
+ spec.swift_version = '5.1'
+ spec.ios.deployment_target = '8.0'
+ spec.osx.deployment_target = '10.9'
+end
diff --git a/XServiceLocator.xcodeproj/Info.plist b/XServiceLocator.xcodeproj/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/XServiceLocator.xcodeproj/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/XServiceLocator.xcodeproj/project.pbxproj b/XServiceLocator.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..a4b4863
--- /dev/null
+++ b/XServiceLocator.xcodeproj/project.pbxproj
@@ -0,0 +1,566 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 509A590724F56B5000AD4B81 /* XServiceLocator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 509A58F624F56B3D00AD4B81 /* XServiceLocator.framework */; };
+ 509A591624F56C4F00AD4B81 /* XServiceLocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 509A591424F56C4F00AD4B81 /* XServiceLocator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 509A591824F56CB200AD4B81 /* TestClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800924E2C2150019F25F /* TestClass.swift */; };
+ 509A591924F56CB200AD4B81 /* TestStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800A24E2C2150019F25F /* TestStruct.swift */; };
+ 509A591A24F56CB200AD4B81 /* TestProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800B24E2C2150019F25F /* TestProtocol.swift */; };
+ 509A591B24F56CB200AD4B81 /* DummyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800C24E2C2150019F25F /* DummyProtocol.swift */; };
+ 509A591C24F56CB200AD4B81 /* XServiceLocatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800D24E2C2150019F25F /* XServiceLocatorTests.swift */; };
+ 50F284CA24F5B36D00F3B5EE /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED7FFB24E2C2150019F25F /* Resolver.swift */; };
+ 50F284CB24F5B36D00F3B5EE /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED7FFC24E2C2150019F25F /* Container.swift */; };
+ 50F284CC24F5B36D00F3B5EE /* Collection+FirstToResolve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED7FFE24E2C2150019F25F /* Collection+FirstToResolve.swift */; };
+ 50F284CD24F5B36D00F3B5EE /* Array+Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED7FFF24E2C2150019F25F /* Array+Resolver.swift */; };
+ 50F284CE24F5B36D00F3B5EE /* AnyServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800124E2C2150019F25F /* AnyServiceFactory.swift */; };
+ 50F284CF24F5B36D00F3B5EE /* ServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800224E2C2150019F25F /* ServiceFactory.swift */; };
+ 50F284D024F5B36D00F3B5EE /* BasicServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ED800324E2C2150019F25F /* BasicServiceFactory.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 509A590824F56B5000AD4B81 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0EADF79A21B1689F00542140 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 509A58F524F56B3D00AD4B81;
+ remoteInfo = XServiceLocator;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 509A58F624F56B3D00AD4B81 /* XServiceLocator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = XServiceLocator.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 509A591424F56C4F00AD4B81 /* XServiceLocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XServiceLocator.h; sourceTree = ""; };
+ 509A591524F56C4F00AD4B81 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 509A591D24F56D5700AD4B81 /* XServiceLocatorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XServiceLocatorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 50ED7FFB24E2C2150019F25F /* Resolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Resolver.swift; sourceTree = ""; };
+ 50ED7FFC24E2C2150019F25F /* Container.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; };
+ 50ED7FFE24E2C2150019F25F /* Collection+FirstToResolve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+FirstToResolve.swift"; sourceTree = ""; };
+ 50ED7FFF24E2C2150019F25F /* Array+Resolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Resolver.swift"; sourceTree = ""; };
+ 50ED800124E2C2150019F25F /* AnyServiceFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyServiceFactory.swift; sourceTree = ""; };
+ 50ED800224E2C2150019F25F /* ServiceFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceFactory.swift; sourceTree = ""; };
+ 50ED800324E2C2150019F25F /* BasicServiceFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicServiceFactory.swift; sourceTree = ""; };
+ 50ED800424E2C2150019F25F /* ServiceLocator.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ServiceLocator.podspec; sourceTree = ""; };
+ 50ED800624E2C2150019F25F /* LinuxMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinuxMain.swift; sourceTree = ""; };
+ 50ED800924E2C2150019F25F /* TestClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestClass.swift; sourceTree = ""; };
+ 50ED800A24E2C2150019F25F /* TestStruct.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestStruct.swift; sourceTree = ""; };
+ 50ED800B24E2C2150019F25F /* TestProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestProtocol.swift; sourceTree = ""; };
+ 50ED800C24E2C2150019F25F /* DummyProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DummyProtocol.swift; sourceTree = ""; };
+ 50ED800D24E2C2150019F25F /* XServiceLocatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XServiceLocatorTests.swift; sourceTree = ""; };
+ 50ED800E24E2C2150019F25F /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
+ 50ED800F24E2C2150019F25F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
+ 50ED801024E2C2150019F25F /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 509A58F324F56B3D00AD4B81 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 509A58FF24F56B5000AD4B81 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 509A590724F56B5000AD4B81 /* XServiceLocator.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 0EADF79921B1689F00542140 = {
+ isa = PBXGroup;
+ children = (
+ 50ED7FF924E2C2150019F25F /* Sources */,
+ 50ED800524E2C2150019F25F /* Tests */,
+ 50ED801024E2C2150019F25F /* LICENSE */,
+ 50ED800F24E2C2150019F25F /* README.md */,
+ 50ED800E24E2C2150019F25F /* Package.swift */,
+ 50ED800424E2C2150019F25F /* ServiceLocator.podspec */,
+ 509A591324F56C4F00AD4B81 /* XServiceLocator */,
+ 50ED804B24E2C3620019F25F /* Products */,
+ 509A58F624F56B3D00AD4B81 /* XServiceLocator.framework */,
+ 509A591D24F56D5700AD4B81 /* XServiceLocatorTests.xctest */,
+ );
+ sourceTree = "";
+ };
+ 509A591324F56C4F00AD4B81 /* XServiceLocator */ = {
+ isa = PBXGroup;
+ children = (
+ 509A591424F56C4F00AD4B81 /* XServiceLocator.h */,
+ 509A591524F56C4F00AD4B81 /* Info.plist */,
+ );
+ path = XServiceLocator;
+ sourceTree = "";
+ };
+ 50ED7FF924E2C2150019F25F /* Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED7FFA24E2C2150019F25F /* XServiceLocator */,
+ );
+ path = Sources;
+ sourceTree = "";
+ };
+ 50ED7FFA24E2C2150019F25F /* XServiceLocator */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED7FFB24E2C2150019F25F /* Resolver.swift */,
+ 50ED7FFC24E2C2150019F25F /* Container.swift */,
+ 50ED7FFD24E2C2150019F25F /* Extensions */,
+ 50ED800024E2C2150019F25F /* ServiceFactory */,
+ );
+ path = XServiceLocator;
+ sourceTree = "";
+ };
+ 50ED7FFD24E2C2150019F25F /* Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED7FFE24E2C2150019F25F /* Collection+FirstToResolve.swift */,
+ 50ED7FFF24E2C2150019F25F /* Array+Resolver.swift */,
+ );
+ path = Extensions;
+ sourceTree = "";
+ };
+ 50ED800024E2C2150019F25F /* ServiceFactory */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED800124E2C2150019F25F /* AnyServiceFactory.swift */,
+ 50ED800224E2C2150019F25F /* ServiceFactory.swift */,
+ 50ED800324E2C2150019F25F /* BasicServiceFactory.swift */,
+ );
+ path = ServiceFactory;
+ sourceTree = "";
+ };
+ 50ED800524E2C2150019F25F /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED800624E2C2150019F25F /* LinuxMain.swift */,
+ 50ED800724E2C2150019F25F /* XServiceLocatorTests */,
+ );
+ path = Tests;
+ sourceTree = "";
+ };
+ 50ED800724E2C2150019F25F /* XServiceLocatorTests */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED800824E2C2150019F25F /* utils */,
+ 50ED800D24E2C2150019F25F /* XServiceLocatorTests.swift */,
+ );
+ path = XServiceLocatorTests;
+ sourceTree = "";
+ };
+ 50ED800824E2C2150019F25F /* utils */ = {
+ isa = PBXGroup;
+ children = (
+ 50ED800924E2C2150019F25F /* TestClass.swift */,
+ 50ED800A24E2C2150019F25F /* TestStruct.swift */,
+ 50ED800B24E2C2150019F25F /* TestProtocol.swift */,
+ 50ED800C24E2C2150019F25F /* DummyProtocol.swift */,
+ );
+ path = utils;
+ sourceTree = "";
+ };
+ 50ED804B24E2C3620019F25F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ path = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 509A58F124F56B3D00AD4B81 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 509A591624F56C4F00AD4B81 /* XServiceLocator.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 509A58F524F56B3D00AD4B81 /* XServiceLocator */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 509A58FB24F56B3D00AD4B81 /* Build configuration list for PBXNativeTarget "XServiceLocator" */;
+ buildPhases = (
+ 509A58F124F56B3D00AD4B81 /* Headers */,
+ 509A58F224F56B3D00AD4B81 /* Sources */,
+ 509A58F324F56B3D00AD4B81 /* Frameworks */,
+ 509A58F424F56B3D00AD4B81 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = XServiceLocator;
+ productName = XServiceLocator;
+ productReference = 509A58F624F56B3D00AD4B81 /* XServiceLocator.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 509A590124F56B5000AD4B81 /* XServiceLocatorTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 509A590A24F56B5000AD4B81 /* Build configuration list for PBXNativeTarget "XServiceLocatorTests" */;
+ buildPhases = (
+ 509A58FE24F56B5000AD4B81 /* Sources */,
+ 509A58FF24F56B5000AD4B81 /* Frameworks */,
+ 509A590024F56B5000AD4B81 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 509A590924F56B5000AD4B81 /* PBXTargetDependency */,
+ );
+ name = XServiceLocatorTests;
+ productName = XServiceLocatorTests;
+ productReference = 509A591D24F56D5700AD4B81 /* XServiceLocatorTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0EADF79A21B1689F00542140 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1160;
+ LastUpgradeCheck = 1000;
+ ORGANIZATIONNAME = "QuickBird Studios GmbH";
+ TargetAttributes = {
+ 509A58F524F56B3D00AD4B81 = {
+ CreatedOnToolsVersion = 11.6;
+ };
+ 509A590124F56B5000AD4B81 = {
+ CreatedOnToolsVersion = 11.6;
+ };
+ };
+ };
+ buildConfigurationList = 0EADF79D21B1689F00542140 /* Build configuration list for PBXProject "XServiceLocator" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 0EADF79921B1689F00542140;
+ productRefGroup = 0EADF79921B1689F00542140;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 509A58F524F56B3D00AD4B81 /* XServiceLocator */,
+ 509A590124F56B5000AD4B81 /* XServiceLocatorTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 509A58F424F56B3D00AD4B81 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 509A590024F56B5000AD4B81 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 509A58F224F56B3D00AD4B81 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 50F284CA24F5B36D00F3B5EE /* Resolver.swift in Sources */,
+ 50F284CB24F5B36D00F3B5EE /* Container.swift in Sources */,
+ 50F284CC24F5B36D00F3B5EE /* Collection+FirstToResolve.swift in Sources */,
+ 50F284CD24F5B36D00F3B5EE /* Array+Resolver.swift in Sources */,
+ 50F284CE24F5B36D00F3B5EE /* AnyServiceFactory.swift in Sources */,
+ 50F284CF24F5B36D00F3B5EE /* ServiceFactory.swift in Sources */,
+ 50F284D024F5B36D00F3B5EE /* BasicServiceFactory.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 509A58FE24F56B5000AD4B81 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 509A591824F56CB200AD4B81 /* TestClass.swift in Sources */,
+ 509A591924F56CB200AD4B81 /* TestStruct.swift in Sources */,
+ 509A591A24F56CB200AD4B81 /* TestProtocol.swift in Sources */,
+ 509A591B24F56CB200AD4B81 /* DummyProtocol.swift in Sources */,
+ 509A591C24F56CB200AD4B81 /* XServiceLocatorTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 509A590924F56B5000AD4B81 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 509A58F524F56B3D00AD4B81 /* XServiceLocator */;
+ targetProxy = 509A590824F56B5000AD4B81 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 0EADF7B521B168A000542140 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 0EADF7B621B168A000542140 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ SWIFT_VERSION = 5.0;
+ VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ 509A58FC24F56B3D00AD4B81 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = 77E79NGPCV;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = XServiceLocator/Info.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.quickbirdstudios.XServiceLocator;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 509A58FD24F56B3D00AD4B81 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ DEFINES_MODULE = YES;
+ DEVELOPMENT_TEAM = 77E79NGPCV;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ INFOPLIST_FILE = XServiceLocator/Info.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.quickbirdstudios.XServiceLocator;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 509A590B24F56B5000AD4B81 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 77E79NGPCV;
+ INFOPLIST_FILE = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.quickbirdstudios.XServiceLocatorTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 509A590C24F56B5000AD4B81 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 77E79NGPCV;
+ INFOPLIST_FILE = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.6;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.quickbirdstudios.XServiceLocatorTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 0EADF79D21B1689F00542140 /* Build configuration list for PBXProject "XServiceLocator" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 0EADF7B521B168A000542140 /* Debug */,
+ 0EADF7B621B168A000542140 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 509A58FB24F56B3D00AD4B81 /* Build configuration list for PBXNativeTarget "XServiceLocator" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 509A58FC24F56B3D00AD4B81 /* Debug */,
+ 509A58FD24F56B3D00AD4B81 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 509A590A24F56B5000AD4B81 /* Build configuration list for PBXNativeTarget "XServiceLocatorTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 509A590B24F56B5000AD4B81 /* Debug */,
+ 509A590C24F56B5000AD4B81 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0EADF79A21B1689F00542140 /* Project object */;
+}
diff --git a/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocator.xcscheme b/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocator.xcscheme
new file mode 100644
index 0000000..22944f7
--- /dev/null
+++ b/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocator.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocatorTests.xcscheme b/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocatorTests.xcscheme
new file mode 100644
index 0000000..3995e58
--- /dev/null
+++ b/XServiceLocator.xcodeproj/xcshareddata/xcschemes/XServiceLocatorTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XServiceLocator/Info.plist b/XServiceLocator/Info.plist
new file mode 100644
index 0000000..9bcb244
--- /dev/null
+++ b/XServiceLocator/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+
+
diff --git a/XServiceLocator/XServiceLocator.h b/XServiceLocator/XServiceLocator.h
new file mode 100644
index 0000000..2fa0f58
--- /dev/null
+++ b/XServiceLocator/XServiceLocator.h
@@ -0,0 +1,18 @@
+//
+// ServiceLocator.h
+// ServiceLocator
+//
+// Copyright Β© 2020 QuickBird Studios GmbH. All rights reserved.
+//
+
+#import
+
+//! Project version number for XServiceLocator.
+FOUNDATION_EXPORT double XServiceLocatorVersionNumber;
+
+//! Project version string for XServiceLocator.
+FOUNDATION_EXPORT const unsigned char XServiceLocatorVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import
+
+