Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
43 changes: 43 additions & 0 deletions Sources/Nodes/Core/AbstractBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,49 @@
lastComponent = newComponent
return build(component: component, dynamicBuildDependency: dynamicBuildDependency)
}

// MARK: - Async

/// Async variant of the factory method ``build(component:dynamicBuildDependency:)``.

Check failure on line 97 in Sources/Nodes/Core/AbstractBuilder.swift

View workflow job for this annotation

GitHub Actions / Lint

A doc comment should be attached to a declaration (orphaned_doc_comment)
///
/// Override this instead of the synchronous version when your builder needs to
/// `await` async dependency resolution.
///
/// - Important: This abstract method must be overridden in subclasses that use async building.
/// This method should never be called directly.
/// The ``AbstractBuilder`` instance calls this method internally.
///
/// - Parameters:
/// - component: The `ComponentType` instance.
/// - dynamicBuildDependency: The `DynamicBuildDependencyType` instance.
///
/// - Returns: A `BuildType` instance (`Flow` object).
// swiftlint:disable:next async_without_await
open func build( // swiftlint:disable:this unavailable_function

Check failure on line 112 in Sources/Nodes/Core/AbstractBuilder.swift

View workflow job for this annotation

GitHub Actions / Lint

SwiftLint rule 'async_without_await' did not trigger a violation in the disabled region; remove the disable command (superfluous_disable_command)
component: ComponentType, // swiftlint:disable:this unused_parameter
dynamicBuildDependency: DynamicBuildDependencyType // swiftlint:disable:this unused_parameter
) async -> BuildType {

Check failure on line 115 in Sources/Nodes/Core/AbstractBuilder.swift

View workflow job for this annotation

GitHub Actions / Lint

Declaration should not be async if it doesn't use await (async_without_await)
preconditionFailure("Method in abstract base class must be overridden")
}

/// Async variant of ``build(_:_:)``. Creates a `ComponentType` instance and passes it to the
/// async ``build(component:dynamicBuildDependency:)`` override.
///
/// - Parameters:
/// - dynamicBuildDependency: The `DynamicBuildDependencyType` instance.
/// - dynamicComponentDependency: The `DynamicComponentDependencyType` instance.
///
/// - Returns: A `BuildType` instance (`Flow` object).
public final func build(
_ dynamicBuildDependency: DynamicBuildDependencyType,
_ dynamicComponentDependency: DynamicComponentDependencyType
) async -> BuildType {
let component: ComponentType = componentFactory(dynamicComponentDependency)
let newComponent: AnyObject = component as AnyObject
assert(newComponent !== lastComponent, "Factory must produce a new component each time it is called")
lastComponent = newComponent
return await build(component: component, dynamicBuildDependency: dynamicBuildDependency)
}
}

// swiftlint:enable period_spacing
45 changes: 45 additions & 0 deletions Sources/Nodes/Core/Config/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,44 @@
return build(component: component)
}

// MARK: - Async

/// Async variant of ``build(component:)``.

Check failure on line 99 in Sources/Nodes/Core/Config/Plugin.swift

View workflow job for this annotation

GitHub Actions / Lint

A doc comment should be attached to a declaration (orphaned_doc_comment)
///
/// Override this instead of the synchronous version when the plugin needs to
/// `await` async dependency resolution.
///
/// - Important: This abstract method must be overridden in subclasses that use async building.
/// This method should never be called directly.
/// The plugin calls this method internally.
///
/// - Parameter component: The `ComponentType` instance.
///
/// - Returns: A `BuildType` instance.
// swiftlint:disable:next async_without_await unused_parameter
open func build(component: ComponentType) async -> BuildType { // swiftlint:disable:this unavailable_function
preconditionFailure("Method in abstract base class must be overridden")
}

/// Async variant of ``create(state:)``. Returns a `BuildType` instance when enabled, otherwise `nil`.
///
/// - Parameter state: The `StateType` instance.
///
/// - Returns: An optional `BuildType` instance.
public func create(state: StateType) async -> BuildType? {
let component: ComponentType = makeComponent()
guard isEnabled(component: component, state: state)
else { return nil }
return await build(component: component)
}

/// Async variant of ``override()``. Returns a `BuildType` instance ignoring whether the plugin is enabled.
///
/// - Returns: A `BuildType` instance.
public func override() async -> BuildType {
await build(component: makeComponent())
}

// MARK: - Access Control: private

private func makeComponent() -> ComponentType {
Expand All @@ -117,4 +155,11 @@
public func create() -> BuildType? {
create(state: ())
}

/// Async convenience for plugins whose `StateType` is `Void`.
///
/// - Returns: An optional `BuildType` instance.
public func create() async -> BuildType? {
await create(state: ())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,26 +106,27 @@ internal final class {{ node_name }}BuilderImp: AbstractBuilder
/// Provides plugin list compatibility.
internal func build(
withListener listener: {{ plugin_list_name }}Listener
) -> {{ plugin_list_name }}Flow {
build(withListener: listener as {{ node_name }}Listener)
) async -> {{ plugin_list_name }}Flow {
await build(withListener: listener as {{ node_name }}Listener)
}
{% endif %}

/// Directs arguments passed through the base class, determining whether each will be provided to its factory build
/// method or the component initializer.
internal func build(
withListener listener: {{ node_name }}Listener
) -> {{ node_name }}Flow {
) async -> {{ node_name }}Flow {
let dynamicBuildDependency: {{ node_name }}DynamicBuildDependency = listener
let dynamicComponentDependency: {{ node_name }}DynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: {{ node_name }}Component,
dynamicBuildDependency: {{ node_name }}DynamicBuildDependency
) -> {{ node_name }}Flow {
) async -> {{ node_name }}Flow {
let listener: {{ node_name }}Listener = dynamicBuildDependency
let store: {{ store_prefix }}Store<
{{ node_name }}State,
Expand Down
13 changes: 7 additions & 6 deletions Sources/NodesGenerator/Resources/Stencils/Builder.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,20 @@ internal final class {{ node_name }}BuilderImp: AbstractBuilder
/// Provides plugin list compatibility.
internal func build(
withListener listener: {{ plugin_list_name }}Listener
) -> {{ plugin_list_name }}Flow {
build(withListener: listener as {{ node_name }}Listener)
) async -> {{ plugin_list_name }}Flow {
await build(withListener: listener as {{ node_name }}Listener)
}
{% endif %}

/// Directs arguments passed through the base class, determining whether each will be provided to its factory build
/// method or the component initializer.
{% if node_name == "App" %}
internal func build() -> {{ node_name }}Flow {
internal func build() async -> {{ node_name }}Flow {
{% else %}
internal func build(
withListener listener: {{ node_name }}Listener{% if not owns_view %}{{ ',' }}
viewController: {{ node_name }}ViewControllable{% endif +%}
) -> {{ node_name }}Flow {
) async -> {{ node_name }}Flow {
{% endif %}
{% if owns_view %}
let dynamicBuildDependency: {{ node_name }}DynamicBuildDependency = listener
Expand All @@ -165,14 +165,15 @@ internal final class {{ node_name }}BuilderImp: AbstractBuilder
let dynamicBuildDependency: {{ node_name }}DynamicBuildDependency = (listener, viewController)
{% endif %}
let dynamicComponentDependency: {{ node_name }}DynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: {{ node_name }}Component,
dynamicBuildDependency: {{ node_name }}DynamicBuildDependency
) -> {{ node_name }}Flow {
) async -> {{ node_name }}Flow {
{% if owns_view %}
let listener: {{ node_name }}Listener = dynamicBuildDependency
{% elif node_name != "App" %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ final class {{ node_name }}BuilderTests: XCTestCase {
}

@MainActor
func testCreate() {
func testCreate() async {
{% if is_nimble_enabled %}
expect { [self] in builder.build(withListener: listenerMock{% if not owns_view %}, viewController: viewControllableMock{% endif %}) } != nil
let result = await builder.build(withListener: listenerMock{% if not owns_view %}, viewController: viewControllableMock{% endif %})
expect(result) != nil
{% else %}
XCTAssertNotNil(builder.build(withListener: listenerMock{% if not owns_view %}, viewController: viewControllableMock{% endif %}))
let result = await builder.build(withListener: listenerMock{% if not owns_view %}, viewController: viewControllableMock{% endif %})
XCTAssertNotNil(result)
{% endif %}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ internal protocol {{ node_name }}Builder: AnyObject {
{% endif %}
func build(
withListener listener: {{ node_name }}Listener
) -> {{ node_name }}Flow
) async -> {{ node_name }}Flow
}
6 changes: 3 additions & 3 deletions Sources/NodesGenerator/Resources/Stencils/Interface.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ internal protocol {{ node_name }}Flow: Flow {}
internal protocol {{ node_name }}Builder: {{ plugin_list_name }}Builder {
func build(
withListener listener: {{ node_name }}Listener
) -> {{ node_name }}Flow
) async -> {{ node_name }}Flow
}
{% else %}
internal protocol {{ node_name }}Builder: AnyObject {
{% if node_name == "App" %}
func build() -> {{ node_name }}Flow
func build() async -> {{ node_name }}Flow
{% else %}
func build(
withListener listener: {{ node_name }}Listener{% if not owns_view %}{{ ',' }}
viewController: {{ node_name }}ViewControllable{% endif +%}
) -> {{ node_name }}Flow
) async -> {{ node_name }}Flow
{% endif %}
}
{% endif %}
3 changes: 2 additions & 1 deletion Sources/NodesGenerator/Resources/Stencils/Plugin.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ internal final class {{ plugin_name }}PluginImp: Plugin
}

/// Implements the factory build method where the instance is initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: {{ plugin_name }}PluginComponent
) -> {{ plugin_name }}Builder {
) async -> {{ plugin_name }}Builder {
{{ plugin_name }}BuilderImp(componentFactory: component.componentFactory)
}
}
Expand Down
16 changes: 10 additions & 6 deletions Sources/NodesGenerator/Resources/Stencils/PluginTests.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,24 @@ final class {{ plugin_name }}PluginTests: XCTestCase {
}

@MainActor
func testCreate() {
func testCreate() async {
{% if is_nimble_enabled %}
expect { [self] in plugin.create() } != nil
let result = await plugin.create()
expect(result) != nil
{% else %}
XCTAssertNotNil(plugin.create())
let result = await plugin.create()
XCTAssertNotNil(result)
{% endif %}
}

@MainActor
func testOverride() {
func testOverride() async {
{% if is_nimble_enabled %}
expect { [self] in plugin.override() } != nil
let result = await plugin.override()
expect(result) != nil
{% else %}
XCTAssertNotNil(plugin.override())
let result = await plugin.override()
XCTAssertNotNil(result)
{% endif %}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,18 @@ internal final class AppBuilderImp: AbstractBuilder

/// Directs arguments passed through the base class, determining whether each will be provided to its factory build
/// method or the component initializer.
internal func build() -> AppFlow {
internal func build() async -> AppFlow {
let dynamicBuildDependency: AppDynamicBuildDependency = ()
let dynamicComponentDependency: AppDynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: AppComponent,
dynamicBuildDependency: AppDynamicBuildDependency
) -> AppFlow {
) async -> AppFlow {
let analytics: AppAnalyticsImp = .init()
let context: AppContextImp = .init(
workers: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ internal protocol AppFlow: Flow {}
/// @mockable
@MainActor
internal protocol AppBuilder: AnyObject {
func build() -> AppFlow
func build() async -> AppFlow
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,18 @@ internal final class RootBuilderImp: AbstractBuilder
/// method or the component initializer.
internal func build(
withListener listener: RootListener
) -> RootFlow {
) async -> RootFlow {
let dynamicBuildDependency: RootDynamicBuildDependency = listener
let dynamicComponentDependency: RootDynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: RootComponent,
dynamicBuildDependency: RootDynamicBuildDependency
) -> RootFlow {
) async -> RootFlow {
let listener: RootListener = dynamicBuildDependency
let store: Store<
RootState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ internal protocol RootFlow: ViewControllableFlow {}
internal protocol RootBuilder: AnyObject {
func build(
withListener listener: RootListener
) -> RootFlow
) async -> RootFlow
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,18 @@ internal final class WindowSceneBuilderImp: AbstractBuilder
internal func build(
withListener listener: WindowSceneListener,
viewController: WindowSceneViewControllable
) -> WindowSceneFlow {
) async -> WindowSceneFlow {
let dynamicBuildDependency: WindowSceneDynamicBuildDependency = (listener, viewController)
let dynamicComponentDependency: WindowSceneDynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: WindowSceneComponent,
dynamicBuildDependency: WindowSceneDynamicBuildDependency
) -> WindowSceneFlow {
) async -> WindowSceneFlow {
let listener: WindowSceneListener = dynamicBuildDependency.0
let viewController: WindowSceneViewControllable = dynamicBuildDependency.1
let analytics: WindowSceneAnalyticsImp = .init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ internal protocol WindowSceneBuilder: AnyObject {
func build(
withListener listener: WindowSceneListener,
viewController: WindowSceneViewControllable
) -> WindowSceneFlow
) async -> WindowSceneFlow
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,18 @@ internal final class WindowBuilderImp: AbstractBuilder
internal func build(
withListener listener: WindowListener,
viewController: WindowViewControllable
) -> WindowFlow {
) async -> WindowFlow {
let dynamicBuildDependency: WindowDynamicBuildDependency = (listener, viewController)
let dynamicComponentDependency: WindowDynamicComponentDependency = ()
return build(dynamicBuildDependency, dynamicComponentDependency)
return await build(dynamicBuildDependency, dynamicComponentDependency)
}

/// Implements the factory build method where instances are initialized.
// swiftlint:disable:next async_without_await
override internal func build(
component: WindowComponent,
dynamicBuildDependency: WindowDynamicBuildDependency
) -> WindowFlow {
) async -> WindowFlow {
let listener: WindowListener = dynamicBuildDependency.0
let viewController: WindowViewControllable = dynamicBuildDependency.1
let analytics: WindowAnalyticsImp = .init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ internal protocol WindowBuilder: AnyObject {
func build(
withListener listener: WindowListener,
viewController: WindowViewControllable
) -> WindowFlow
) async -> WindowFlow
}
Loading
Loading