Skip to content

Commit df5fa0e

Browse files
committed
feat: Instance Storage
1 parent f1d764d commit df5fa0e

17 files changed

+314
-47
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import PackageDescription
66
let package = Package(
77
name: "Astroject",
88
platforms: [
9-
.iOS(.v13)
9+
.iOS(.v16),
10+
.macOS(.v13)
1011
],
1112
products: [
1213
// Products define the executables and libraries a package produces, making them visible to other packages.

Sources/Astroject/Container.swift

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,44 +9,51 @@ import Foundation
99

1010
// ✅ Registration
1111
// ✅ Resolution
12-
// TODO: InstanceStore
12+
// ✅ InstanceStore
13+
// TODO: Error Handling
14+
// TODO: Write Unit Tests
15+
// TODO: Is this robust and sufficient enough to support my needs?
1316

1417
public class Container {
15-
var factories: ThreadSafeDictionary<FactoryKey, any FactoryRegistrable> = .init()
18+
var factories: ThreadSafeDictionary<ProductKey, any Registrable> = .init()
1619

1720
public init() {}
1821

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)
22+
@discardableResult
23+
public func registerAsync<Product>(_ productType: Product.Type, name: String? = nil, factory block: @escaping (Resolver) async -> Product) -> Registration<Product> {
24+
let factoryKey = ProductKey(productType: productType, name: name)
2125
let factory = Factory.async(block)
22-
let registration = FactoryRegistration(factory: factory)
26+
let registration = Registration(factory: factory, container: self)
2327
factories.insert(registration, for: factoryKey)
28+
return registration
2429
}
2530

26-
public func register<Product>(_ productType: Product.Type, name: String? = nil, factory block: @escaping (Resolver) -> Product) {
27-
let factoryKey = FactoryKey(productType: productType, name: name)
31+
@discardableResult
32+
public func register<Product>(_ productType: Product.Type, name: String? = nil, factory block: @escaping (Resolver) -> Product) -> Registration<Product> {
33+
let factoryKey = ProductKey(productType: productType, name: name)
2834
let factory = Factory.sync(block)
29-
let registration = FactoryRegistration(factory: factory)
35+
let registration = Registration(factory: factory, container: self)
3036
factories.insert(registration, for: factoryKey)
37+
return registration
3138
}
3239
}
3340

3441
extension Container: Resolver {
3542
public func resolve<Product>(_ productType: Product.Type, name: String?) -> Product? {
3643
let registration = registration(for: productType, with: name)
37-
let product = registration?.factory.make(self)
44+
let product = registration?.resolve()
3845
return product
3946
}
4047

4148
public func resolveAsync<Product>(_ productType: Product.Type, name: String?) async -> Product? {
4249
let registration = registration(for: productType, with: name)
43-
let product = await registration?.factory.makeAsync(self)
50+
let product = await registration?.resolveAsync()
4451
return product
4552
}
4653

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>
54+
func registration<Product>(for productType: Product.Type, with name: String?) -> Registration<Product>? {
55+
let factoryKey = ProductKey(productType: productType, name: name)
56+
let registration = factories.getValue(for: factoryKey) as? Registration<Product>
5057
return registration
5158
}
5259
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// AstrojectError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/27/25.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents errors that can occur within the Astroject dependency injection container.
11+
public enum AstrojectError: Error {
12+
/// Errors related to dependency resolution.
13+
case resolution(ResolutionError)
14+
/// Errors related to factory creation.
15+
case factory(FactoryError)
16+
/// Errors related to dependency registration.
17+
case registration(RegistrationError)
18+
/// Errors related to instance management.
19+
case instance(InstanceError)
20+
/// Errors that occur internally within the library.
21+
case internalError(Error)
22+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// FactoryError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/27/25.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents errors that can occur during factory creation.
11+
public enum FactoryError: Error {
12+
/// An error that occurred within the factory closure.
13+
case underlyingError(Error)
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// InstanceError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/27/25.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents errors that can occur during instance management.
11+
public enum InstanceError: Error {
12+
/// An instance is expected but not found.
13+
case noInstance
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// RegistrationError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/27/25.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents errors that can occur during dependency registration.
11+
public enum RegistrationError: Error {
12+
/// A registration is attempted with a ProductKey that already exists.
13+
case alreadyRegistered
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// ResolutionError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/27/25.
6+
//
7+
8+
import Foundation
9+
10+
/// Represents errors that can occur during dependency resolution.
11+
public enum ResolutionError: Error {
12+
/// A dependency is requested but not registered.
13+
case noRegistrationFound
14+
/// Circular dependency detected.
15+
case circularDependency
16+
/// Calling a sync function when an async function should be called instead.
17+
case asyncResolutionRequired
18+
}

Sources/Astroject/FactoryRegistration.swift

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// Composite.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/26/25.
6+
//
7+
8+
import Foundation
9+
10+
public class Composite<Product>: Instance {
11+
var instances: [any Instance<Product>] = []
12+
13+
public init(instances: [any Instance<Product>]) {
14+
self.instances = instances
15+
}
16+
17+
public func get() -> Product? {
18+
instances.compactMap { $0.get() }.first
19+
}
20+
21+
public func set(_ product: Product) {
22+
instances.forEach { $0.set(product) }
23+
}
24+
25+
public func release() {
26+
instances.forEach { $0.release() }
27+
}
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// Instance.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 2/26/25.
6+
//
7+
8+
import Foundation
9+
10+
public protocol Instance<Product> {
11+
associatedtype Product
12+
13+
func get() -> Product?
14+
func set(_ product: Product)
15+
func release()
16+
}

0 commit comments

Comments
 (0)