Skip to content

Commit f1d764d

Browse files
committed
feat: Factories, Container, Thread Safety, Resolvers, & Registration
1 parent 02363bc commit f1d764d

14 files changed

+378
-8
lines changed

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import PackageDescription
55

66
let package = Package(
77
name: "Astroject",
8+
platforms: [
9+
.iOS(.v13)
10+
],
811
products: [
912
// Products define the executables and libraries a package produces, making them visible to other packages.
1013
.library(

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Astroject
2+
Library for Dependency Injection with Swift concurrency in mind.
3+
4+
## Overview
5+
Astroject is an advanced dependency injection library designed to help make dependency trees manageable and sufficient from the smallest to the largest scaled software project.
6+
7+
## Features
8+
- **Thread Safety**
9+
- **Swift Concurrency**
10+
- **Ease of Use & Adaptation**
11+

Sources/Astroject/.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//
2+
// File.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation

Sources/Astroject/Astroject.swift

Lines changed: 0 additions & 2 deletions
This file was deleted.

Sources/Astroject/Container.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// Container.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
// ✅ Registration
11+
// ✅ Resolution
12+
// TODO: InstanceStore
13+
14+
public class Container {
15+
var factories: ThreadSafeDictionary<FactoryKey, any FactoryRegistrable> = .init()
16+
17+
public init() {}
18+
19+
public func registerAsync<Product>(_ productType: Product.Type, name: String? = nil, factory block: @escaping (Resolver) async -> Product) {
20+
let factoryKey = FactoryKey(productType: productType, name: name)
21+
let factory = Factory.async(block)
22+
let registration = FactoryRegistration(factory: factory)
23+
factories.insert(registration, for: factoryKey)
24+
}
25+
26+
public func register<Product>(_ productType: Product.Type, name: String? = nil, factory block: @escaping (Resolver) -> Product) {
27+
let factoryKey = FactoryKey(productType: productType, name: name)
28+
let factory = Factory.sync(block)
29+
let registration = FactoryRegistration(factory: factory)
30+
factories.insert(registration, for: factoryKey)
31+
}
32+
}
33+
34+
extension Container: Resolver {
35+
public func resolve<Product>(_ productType: Product.Type, name: String?) -> Product? {
36+
let registration = registration(for: productType, with: name)
37+
let product = registration?.factory.make(self)
38+
return product
39+
}
40+
41+
public func resolveAsync<Product>(_ productType: Product.Type, name: String?) async -> Product? {
42+
let registration = registration(for: productType, with: name)
43+
let product = await registration?.factory.makeAsync(self)
44+
return product
45+
}
46+
47+
func registration<Product>(for productType: Product.Type, with name: String?) -> FactoryRegistration<Product>? {
48+
let factoryKey = FactoryKey(productType: productType, name: name)
49+
let registration = factories.getValue(for: factoryKey) as? FactoryRegistration<Product>
50+
return registration
51+
}
52+
}

Sources/Astroject/Factory.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// Factory.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
enum Factory<Product> {
11+
case sync((Resolver) -> Product)
12+
case async((Resolver) async -> Product)
13+
14+
var isSync: Bool {
15+
switch self {
16+
case .sync:
17+
return true
18+
case .async:
19+
return false
20+
}
21+
}
22+
23+
var isAsync: Bool {
24+
return !isSync
25+
}
26+
27+
func make(_ resolver: Resolver) -> Product {
28+
switch self {
29+
case .sync(let closure):
30+
closure(resolver)
31+
case .async:
32+
fatalError("Factory Method is Async. Try calling the async version of this method.")
33+
}
34+
}
35+
36+
func makeAsync(_ resolver: Resolver) async -> Product {
37+
switch self {
38+
case .sync(let closure):
39+
closure(resolver)
40+
case .async(let closure):
41+
await closure(resolver)
42+
}
43+
}
44+
}

Sources/Astroject/FactoryKey.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// FactoryKey.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
struct FactoryKey {
11+
let productType: Any.Type
12+
let name: String?
13+
14+
init(productType: Any.Type, name: String? = nil) {
15+
self.productType = productType
16+
self.name = name
17+
}
18+
}
19+
20+
extension FactoryKey: Hashable {
21+
func hash(into hasher: inout Hasher) {
22+
ObjectIdentifier(self.productType).hash(into: &hasher)
23+
self.name?.hash(into: &hasher)
24+
}
25+
}
26+
27+
extension FactoryKey: Equatable {
28+
static func == (lhs: FactoryKey, rhs: FactoryKey) -> Bool {
29+
lhs.productType == rhs.productType && lhs.name == rhs.name
30+
}
31+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// FactoryRegistrable.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
protocol FactoryRegistrable {
11+
associatedtype Product
12+
13+
var factory: Factory<Product> { get }
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// FactoryRegistration.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
struct FactoryRegistration<Product>: FactoryRegistrable {
11+
let factory: Factory<Product>
12+
13+
init(factory: Factory<Product>) {
14+
self.factory = factory
15+
}
16+
}

Sources/Astroject/Resolver.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// Resolver.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/25/25.
6+
//
7+
8+
import Foundation
9+
10+
public protocol Resolver {
11+
func resolve<Product>(_ productType: Product.Type) -> Product?
12+
func resolve<Product>(_ productType: Product.Type, name: String?) -> Product?
13+
14+
func resolveAsync<Product>(_ productType: Product.Type) async -> Product?
15+
func resolveAsync<Product>(_ productType: Product.Type, name: String?) async -> Product?
16+
}
17+
18+
public extension Resolver {
19+
func resolve<Product>(_ productType: Product.Type) -> Product? {
20+
resolve(productType, name: nil)
21+
}
22+
23+
func resolveAsync<Product>(_ productType: Product.Type) async -> Product? {
24+
await resolveAsync(productType, name: nil)
25+
}
26+
}

0 commit comments

Comments
 (0)