From 20aa2b9a147577e1dc2c5b83b9fc6b7455e1722c Mon Sep 17 00:00:00 2001 From: Stefan Kofler Date: Mon, 14 Jan 2019 11:44:27 +0100 Subject: [PATCH] first commit --- DI.playground/Contents.swift | 233 ++++++++++++++++++ DI.playground/contents.xcplayground | 4 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 8924 bytes 5 files changed, 252 insertions(+) create mode 100644 DI.playground/Contents.swift create mode 100644 DI.playground/contents.xcplayground create mode 100644 DI.playground/playground.xcworkspace/contents.xcworkspacedata create mode 100644 DI.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 DI.playground/playground.xcworkspace/xcuserdata/stefankofler.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/DI.playground/Contents.swift b/DI.playground/Contents.swift new file mode 100644 index 0000000..b4ba9e7 --- /dev/null +++ b/DI.playground/Contents.swift @@ -0,0 +1,233 @@ +import UIKit +import XCTest + +class Repository { + func getAll() -> [Type] { + fatalError() + } +} + +class DatabaseRepository: Repository {} + +struct Article: Equatable { + let title: String +} + +class Basket { + var articles = [Article]() +} + +class BasketService { + private let repository: Repository
+ + init(repository: Repository
= DatabaseRepository()) { + self.repository = repository + } + + func addAllArticles(to basket: Basket) { + let allArticles = repository.getAll() + basket.articles.append(contentsOf: allArticles) + } +} + +class MockRepository: Repository { + + var objects: [Type] + + init(objects: [Type]) { + self.objects = objects + } + + override func getAll() -> [Type] { + return objects + } +} + +class BasketServiceTests: XCTestCase { + func testAddAllArticles() { + let expectedArticle = Article(title: "Article 1") + let mockRepository = MockRepository
(objects: [expectedArticle]) + let basketService = BasketService(repository: mockRepository) + let basket = Basket() + + basketService.addAllArticles(to: basket) + + XCTAssertEqual(basket.articles.count, 1) + XCTAssertEqual(basket.articles[0], expectedArticle) + } +} + +BasketServiceTests.defaultTestSuite.run() + +class BasketViewController: UIViewController { + var basketService: BasketService = BasketService() +} + +//let basketViewController = BasketViewController() +//basketViewController.basketService = BasketService() + +protocol BasketFactory { + func makeBasketService() -> BasketService + func makeBasketViewController() -> BasketViewController +} + +class DefaultBasketFactory: BasketFactory { + + func makeBasketService() -> BasketService { + let repository = makeArticleRepository() + return BasketService(repository: repository) + } + + func makeBasketViewController() -> BasketViewController { + let basketViewController = BasketViewController() + basketViewController.basketService = makeBasketService() + return basketViewController + } + + // MARK: Private factory methods + + private func makeArticleRepository() -> Repository
{ + return DatabaseRepository() + } + +} + +let factory = DefaultBasketFactory() + +let basketViewController2 = factory.makeBasketViewController() + +protocol Resolver { + func resolve(_ type: ServiceType.Type) -> ServiceType +} + +struct Container: Resolver { + + let factories: [AnyServiceFactory] + + init() { + self.factories = [] + } + + private init(factories: [AnyServiceFactory]) { + self.factories = factories + } + + // MARK: Register + + func register(_ interface: T.Type, instance: T) -> Container { + return register(interface) { _ in instance } + } + + func register(_ type: ServiceType.Type, _ factory: @escaping (Resolver) -> ServiceType) -> Container { + assert(!factories.contains(where: { $0.supports(type) })) + + let newFactory = BasicServiceFactory(type, factory: { resolver in + factory(resolver) + }) + return .init(factories: factories + [AnyServiceFactory(newFactory)]) + } + + // MARK: Resolver + + func resolve(_ type: ServiceType.Type) -> ServiceType { + guard let factory = factories.first(where: { $0.supports(type) }) else { + fatalError("No suitable factory found") + } + return factory.resolve(self) + } + + func factory(for type: ServiceType.Type) -> () -> ServiceType { + guard let factory = factories.first(where: { $0.supports(type) }) else { + fatalError("No suitable factory found") + } + + return { factory.resolve(self) } + } +} + +protocol ServiceFactory { + associatedtype ServiceType + + func resolve(_ resolver: Resolver) -> ServiceType +} + +extension ServiceFactory { + func supports(_ type: T.Type) -> Bool { + return type == ServiceType.self + } +} + +extension Resolver { + func factory(for type: ServiceType.Type) -> () -> ServiceType { + return { self.resolve(type) } + } +} + +struct BasicServiceFactory: ServiceFactory { + private let factory: (Resolver) -> ServiceType + + init(_ type: ServiceType.Type, factory: @escaping (Resolver) -> ServiceType) { + self.factory = factory + } + + func resolve(_ resolver: Resolver) -> ServiceType { + return factory(resolver) + } +} + +final class AnyServiceFactory { + private let _resolve: (Resolver) -> Any + private let _supports: (Any.Type) -> Bool + + init(_ serviceFactory: T) { + self._resolve = { resolver -> Any in + serviceFactory.resolve(resolver) + } + self._supports = { $0 == T.ServiceType.self } + } + + func resolve(_ resolver: Resolver) -> ServiceType { + return _resolve(resolver) as! ServiceType + } + + func supports(_ type: ServiceType.Type) -> Bool { + return _supports(type) + } +} + +let basketContainer = Container() + .register(Bundle.self, instance: Bundle.main) + .register(Repository
.self) { _ in DatabaseRepository() } + .register(BasketService.self) { resolver in + let repository = resolver.resolve(Repository
.self) + return BasketService(repository: repository) + } + .register(BasketViewController.self) { resolver in + let basketViewController = BasketViewController() + basketViewController.basketService = resolver.resolve(BasketService.self) + return basketViewController + } + +let basketViewController = basketContainer.resolve(BasketViewController.self) + +let basketVCFactory = basketContainer.factory(for: BasketViewController.self) +let basketViewController3 = basketVCFactory() + +class HomeViewController: UIViewController { + var basketViewControllerFactory: () -> BasketViewController = { fatalError("Factory must be injected") } + + func showBasketView() { + let basketViewController = basketViewControllerFactory() + self.present(basketViewController, animated: true, completion: nil) + } +} + +let mainContainer = basketContainer + .register(HomeViewController.self) { resolver in + let homeViewController = HomeViewController() + homeViewController.basketViewControllerFactory = resolver.factory(for: BasketViewController.self) + return homeViewController + } + +let homeVC = mainContainer.resolve(HomeViewController.self) +homeVC.showBasketView() diff --git a/DI.playground/contents.xcplayground b/DI.playground/contents.xcplayground new file mode 100644 index 0000000..9f5f2f4 --- /dev/null +++ b/DI.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/DI.playground/playground.xcworkspace/contents.xcworkspacedata b/DI.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/DI.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DI.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DI.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/DI.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DI.playground/playground.xcworkspace/xcuserdata/stefankofler.xcuserdatad/UserInterfaceState.xcuserstate b/DI.playground/playground.xcworkspace/xcuserdata/stefankofler.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..8aadacf81f127d466a8cde02ae0666946a834822 GIT binary patch literal 8924 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 V1BVP8(Vg>Y_w)JQIe%d4{{j#F;yVBU literal 0 HcmV?d00001