Skip to content

Commit

Permalink
Merge pull request #197 from cashapp/bradfol/mainactor-servicecollection
Browse files Browse the repository at this point in the history
MainActor support for ServiceCollections
  • Loading branch information
bradfol authored Aug 30, 2024
2 parents 1f54550 + 0611186 commit a953283
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 5 deletions.
1 change: 1 addition & 0 deletions CLI/Sources/KnitCodeGen/UnitTestSourceFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public enum UnitTestSourceFile {

private static func makeCollectionAssert() throws -> FunctionDeclSyntax {
let string: SyntaxNodeString = #"""
@MainActor
func assertCollectionResolves<T>(
_ type: T.Type,
count expectedCount: Int,
Expand Down
1 change: 1 addition & 0 deletions CLI/Tests/KnitCodeGenTests/ConfigurationSetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ final class ConfigurationSetTests: XCTestCase {
line: line
)
}
@MainActor
func assertCollectionResolves<T>(
_ type: T.Type,
count expectedCount: Int,
Expand Down
21 changes: 21 additions & 0 deletions Example/KnitExample/KnitExampleAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,22 @@ final class KnitExampleAssembly: ModuleAssembly {
ClosureService(closure: arg1)
}

container.register(
MainActorService.self,
mainActorFactory: { _ in
MainActorService()
}
)

container.autoregisterIntoCollection(ExampleService.self, initializer: ExampleService.init)

container.registerIntoCollection(
MainActorService.self,
factory: { _ in
MainActorService()
}
)

#if DEBUG
container.autoregister(DebugService.self, initializer: DebugService.init)
#endif
Expand Down Expand Up @@ -81,3 +95,10 @@ final class ClosureService {
}

struct DebugService { }

@MainActor
final class MainActorService {
init() { }

var title: String { "Example String" }
}
12 changes: 10 additions & 2 deletions Sources/Knit/ServiceCollection/Container+ServiceCollection.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ extension Container {
@discardableResult
public func registerIntoCollection<Service>(
_ service: Service.Type,
factory: @escaping (Resolver) -> Service
factory: @escaping @MainActor (Resolver) -> Service
) -> ServiceEntry<Service> {
self.register(service, name: makeUniqueCollectionRegistrationName(), factory: factory)
self.register(
service,
name: makeUniqueCollectionRegistrationName(),
factory: { resolver in
MainActor.assumeIsolated {
return factory(resolver)
}
}
)
}

/// Registers a service factory into a collection.
Expand Down
12 changes: 10 additions & 2 deletions Sources/Knit/ServiceCollection/Container+ServiceCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,17 @@ extension Container {
@discardableResult
public func registerIntoCollection<Service>(
_ service: Service.Type,
factory: @escaping (Resolver) -> Service
factory: @escaping @MainActor (Resolver) -> Service
) -> ServiceEntry<Service> {
self.register(service, name: makeUniqueCollectionRegistrationName(), factory: factory)
self.register(
service,
name: makeUniqueCollectionRegistrationName(),
factory: { resolver in
MainActor.assumeIsolated {
return factory(resolver)
}
}
)
}

/// Registers a service factory into a collection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extension Resolver {
/// - Parameter serviceType: The service types to resolve.
/// - Returns: A ``ServiceCollection`` containing all registered services,
/// or an empty collection if no services were registered.
@MainActor
public func resolveCollection<Service>(_ serviceType: Service.Type) -> ServiceCollection<Service> {
resolve(ServiceCollection<Service>.self) ?? .init(parent: nil, entries: [])
}
Expand Down
17 changes: 16 additions & 1 deletion Tests/KnitTests/ServiceCollectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ final class ServiceCollectorTests: XCTestCase {

// MARK: - Tests - registerIntoCollection

@MainActor
func test_registerIntoCollection() {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -78,6 +79,7 @@ final class ServiceCollectorTests: XCTestCase {
)
}

@MainActor
func test_registerIntoCollection_emptyWithBehavior() {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -86,6 +88,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssertEqual(collection.entries.count, 0)
}

@MainActor
func test_registerIntoCollection_emptyWithoutBehavior() {
let container = Container()

Expand All @@ -95,6 +98,7 @@ final class ServiceCollectorTests: XCTestCase {

/// ``ServiceCollector`` shouldn't preclude users from registering their own separate ``Array<Service>``.
/// A conflict here would be confusing and surprising to the user.
@MainActor
func test_registerIntoCollection_doesntConflictWithArray() throws {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -116,6 +120,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssert(array.first is ServiceB)
}

@MainActor
func test_registerIntoCollection_doesntImplicitlyAggregateInstances() throws {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -137,6 +142,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssert(container.resolve(ServiceProtocol.self) is ServiceB)
}

@MainActor
func test_registerIntoCollection_allowsDuplicates() {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -156,6 +162,7 @@ final class ServiceCollectorTests: XCTestCase {

// MARK: - Tests - autoregisterIntoCollection

@MainActor
func test_autoregisterIntoCollection() {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -172,6 +179,7 @@ final class ServiceCollectorTests: XCTestCase {
}

// High-arity overloads are generated by a script. Ensure they work as expected.
@MainActor
func test_autoregisterIntoCollection_highArityOverloads() {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -195,6 +203,7 @@ final class ServiceCollectorTests: XCTestCase {

// MARK: - Tests - Object Scopes

@MainActor
func test_registerIntoCollection_supportsTransientScopedObjects() throws {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -214,6 +223,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssert(instance1 !== instance2)
}

@MainActor
func test_registerIntoCollection_supportsContainerScopedObjects() throws {
let container = Container()
container.addBehavior(ServiceCollector())
Expand All @@ -233,6 +243,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssert(instance1 === instance2)
}

@MainActor
func test_registerIntoCollection_supportsWeakScopedObjects() throws {
let container = Container()
container.addBehavior(ServiceCollector())
Expand Down Expand Up @@ -264,6 +275,7 @@ final class ServiceCollectorTests: XCTestCase {
XCTAssertEqual(factoryCallCount, 2)
}

@MainActor
func test_parentChildContainersWithAssemblers() {
let parent = ModuleAssembler([AssemblyA()])
let child = ModuleAssembler(parent: parent, [AssemblyB()])
Expand All @@ -288,6 +300,7 @@ final class ServiceCollectorTests: XCTestCase {

}

@MainActor
func test_childWithEmptyParent() {
let parent = ModuleAssembler([AssemblyC()])
let child = ModuleAssembler(parent: parent, [AssemblyB()])
Expand All @@ -303,7 +316,8 @@ final class ServiceCollectorTests: XCTestCase {
1
)
}


@MainActor
func test_emptyChildWithParent() {
let parent = ModuleAssembler([AssemblyB()])
let child = ModuleAssembler(parent: parent, [AssemblyC()])
Expand All @@ -321,6 +335,7 @@ final class ServiceCollectorTests: XCTestCase {
)
}

@MainActor
func test_grandparentRelationship() {
let grandParent = ModuleAssembler([AssemblyA()])
let parent = ModuleAssembler(parent: grandParent, [AssemblyC()])
Expand Down

0 comments on commit a953283

Please sign in to comment.