Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defining dependencies with arguments #49

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Implemented factories with a custom argument
  • Loading branch information
brototyp committed Dec 29, 2022
commit 785c9f2b447427913ed2a6cb1fb54e5a5570a3c4
12 changes: 6 additions & 6 deletions DIKit/Sources/Component/Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@
//
// Copyright © 2018 Ben John. All rights reserved.

public typealias ComponentFactory = () -> Any
public typealias ComponentFactory = (Any?) -> Any

class Component<T>: ComponentProtocol {
class Component<A, T>: ComponentProtocol {
let lifetime: Lifetime
let identifier: AnyHashable
let type: Any.Type
let componentFactory: ComponentFactory

init(lifetime: Lifetime, factory: @escaping () -> T) {
init(lifetime: Lifetime, factory: @escaping (_ argument: A) -> T) {
self.lifetime = lifetime
self.identifier = ComponentIdentifier(type: T.self)
self.type = T.self
self.componentFactory = { factory() }
self.componentFactory = { factory($0 as! A) } // swiftlint:disable:this force_cast
}

init(lifetime: Lifetime, tag: AnyHashable, factory: @escaping () -> T) {
init(lifetime: Lifetime, tag: AnyHashable, factory: @escaping (_ argument: A) -> T) {
self.lifetime = lifetime
self.identifier = ComponentIdentifier(tag: tag, type: T.self)
self.type = T.self
self.componentFactory = { factory() }
self.componentFactory = { factory($0 as! A) } // swiftlint:disable:this force_cast
}
}

Expand Down
8 changes: 7 additions & 1 deletion DIKit/Sources/Container/DependencyContainer+Register.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extension DependencyContainer {
/// - Parameters:
/// - lifetime: The *scope* of the `Component`, defaults to `Lifetime.singleton`.
/// - factory: The *factory* for the initialization of the `Component`.
public func register<T>(lifetime: Lifetime = .singleton, _ factory: @escaping () -> T) {
public func register<A, T>(lifetime: Lifetime = .singleton, _ factory: @escaping (A) -> T) {
let component = Component(lifetime: lifetime, factory: factory)
register(component)
}
Expand All @@ -38,4 +38,10 @@ extension DependencyContainer {
self.componentStack[component.identifier] = component
}
}

/// Convenience function without any arguments
public func register<T>(lifetime: Lifetime = .singleton, _ factory: @escaping () -> T) {
let newFactory: (Any) -> T = { _ in factory() }
register(lifetime: lifetime, newFactory)
}
}
17 changes: 14 additions & 3 deletions DIKit/Sources/Container/DependencyContainer+Resolve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ extension DependencyContainer {
///
/// - Parameter tag: An optional *tag* to identify the Component. `nil` per default.
/// - Returns: The resolved `Optional<Component<T>>`.
func _resolve<T>(tag: AnyHashable? = nil) -> T? {
func _resolve<A, T>(_ argument: A, tag: AnyHashable? = nil) -> T? {
let identifier = ComponentIdentifier(tag: tag, type: T.self)
guard let foundComponent = self.componentStack[identifier] else {
return nil
}
if foundComponent.lifetime == .factory {
return foundComponent.componentFactory() as? T
return foundComponent.componentFactory(argument) as? T
}
if let instanceOfComponent = self.instanceStack[identifier] as? T {
return instanceOfComponent
}
let instance = foundComponent.componentFactory() as! T
let instance = foundComponent.componentFactory(argument) as! T
self.instanceStack[identifier] = instance
return instance
}
Expand All @@ -48,10 +48,21 @@ extension DependencyContainer {
/// - Parameter tag: An optional *tag* to identify the Component. `nil` per default.
///
/// - Returns: The resolved `Component<T>`.
public func resolve<A, T>(_ argument: A, tag: AnyHashable? = nil) -> T {
if let t: T = _resolve(argument, tag: tag) {
return t
}
fatalError("Component `\(String(describing: T.self))` could not be resolved.")
}

/// Convenience function without any arguments
public func resolve<T>(tag: AnyHashable? = nil) -> T {
if let t: T = _resolve(tag: tag) {
return t
}
fatalError("Component `\(String(describing: T.self))` could not be resolved.")
}
func _resolve<T>(tag: AnyHashable? = nil) -> T? {
_resolve((), tag: tag)
}
}
4 changes: 4 additions & 0 deletions DIKit/Sources/DIKit+Inject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public struct Inject<Component> {
public init(tag: AnyHashable? = nil) {
self.wrappedValue = resolve(tag: tag)
}

public init<A>(_ argument: A) {
self.wrappedValue = resolve(argument)
}
}

/// A property wrapper (SE-0258) to make a `Optional<Component>` injectable
Expand Down
7 changes: 7 additions & 0 deletions DIKit/Sources/DIKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
/// - Returns: The resolved `Component<T>`.
public func resolve<T>(tag: AnyHashable? = nil) -> T { DependencyContainer.shared.resolve(tag: tag) }

public func resolve<A, T>(_ argument: A, tag: AnyHashable? = nil) -> T { DependencyContainer.shared.resolve(argument, tag: tag) }

/// Resolves nil safe given `Component<T>`.
///
/// - Parameter tag: An optional *tag* to identify the Component. `nil` per default.
Expand All @@ -21,3 +23,8 @@ public func resolveOptional<T>(tag: AnyHashable? = nil) -> T? {
guard DependencyContainer.shared.resolvable(type: T.self, tag: tag) else { return nil }
return DependencyContainer.shared._resolve(tag: tag)
}

public func resolveOptional<A, T>(_ argument: A, tag: AnyHashable? = nil) -> T? {
guard DependencyContainer.shared.resolvable(type: T.self, tag: tag) else { return nil }
return DependencyContainer.shared._resolve(argument, tag: tag)
}
8 changes: 8 additions & 0 deletions DIKit/Sources/DIKitDSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public func resolvable<T>(lifetime: Lifetime = .singleton, _ factory: @escaping
Component(lifetime: lifetime, factory: factory) as ComponentProtocol
}

public func resolvable<A, T>(lifetime: Lifetime = .singleton, _ factory: @escaping (_ argument: A) -> T) -> ComponentProtocol {
Component(lifetime: lifetime, factory: factory) as ComponentProtocol
}

public func resolvable<T>(
lifetime: Lifetime = .singleton,
tag: AnyHashable,
Expand All @@ -53,6 +57,10 @@ public func factory<T>(factory: @escaping () -> T) -> [ComponentProtocol] {
[resolvable(lifetime: .factory, factory)]
}

public func factory<A, T>(factory: @escaping (_ argument: A) -> T) -> [ComponentProtocol] {
[resolvable(lifetime: .factory, factory)]
}

public func factory<T>(tag: AnyHashable, factory: @escaping () -> T) -> [ComponentProtocol] {
[resolvable(lifetime: .factory, tag: tag, factory)]
}
Expand Down
6 changes: 4 additions & 2 deletions DIKitExample/Sources/DependencyContainer+App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import DIKit
public extension DependencyContainer {
static var app = module {
// We create two different kind of storages. One for both kind of contexts.
factory(tag: StorageContext.systemdata) { LocalStorage() as LocalStorageProtocol }
factory(tag: StorageContext.userdata) { LocalStorage() as LocalStorageProtocol }
factory(tag: StorageContext.systemdata) { LocalStorage(name: "system") as LocalStorageProtocol }
factory(tag: StorageContext.userdata) { LocalStorage(name: "userdata") as LocalStorageProtocol }

factory { name in LocalStorage(name: name ?? "default") as LocalStorageProtocol }
}
}
4 changes: 3 additions & 1 deletion DIKitExample/Sources/LocalStorageLayer/LocalStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ class LocalStorage: LocalStorageProtocol {
var id: ObjectIdentifier {
return ObjectIdentifier.init(self)
}
let name: String

init() {
init(name: String) {
self.name = name
print("LocalStorage init")
print("LocalStorage instance \(ObjectIdentifier.init(self))")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@

protocol LocalStorageProtocol {
var id: ObjectIdentifier { get }
var name: String { get }
}
3 changes: 3 additions & 0 deletions DIKitExample/Sources/Views/FirstViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import DIKitExampleBackend
class FirstViewController: UIViewController {
// MARK: - DIKit
@Inject var backend: BackendProtocol
@Inject("Local Storage with a custom name") var localStorage: LocalStorageProtocol

// MARK: - View lifecycle
override func viewWillAppear(_ animated: Bool) {
let result = backend.fetch()
print(result)

print(localStorage.name)
}
}