RIBs/Classes/ComponentizedBuilder.swift (43 lines of code) (raw):
//
// Copyright (c) 2017. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Utility that instantiates a RIB and sets up its internal wirings.
/// This class ensures the strict one to one relationship between a
/// new instance of the RIB and a single new instance of the component.
/// Every time a new RIB is built a new instance of the corresponding
/// component is also instantiated.
///
/// This is the most generic version of the builder class that supports
/// both dynamic dependencies injected when building the RIB as well
/// as dynamic dependencies for instantiating the component. For more
/// convenient base class, please refer to `SimpleComponentizedBuilder`.
///
/// - note: Subclasses should override the `build(with)` method to
/// implement the actual RIB building logic, with the given component
/// and dynamic dependency.
/// - SeeAlso: SimpleComponentizedBuilder
open class ComponentizedBuilder<Component, Router, DynamicBuildDependency, DynamicComponentDependency>: Buildable {
// Builder should not directly retain an instance of the component.
// That would make the component's lifecycle longer than the built
// RIB. Instead, whenever a new instance of the RIB is built, a new
// instance of the DI component should also be instantiated.
/// Initializer.
///
/// - parameter componentBuilder: The closure to instantiate a new
/// instance of the DI component that should be paired with this RIB.
public init(componentBuilder: @escaping (DynamicComponentDependency) -> Component) {
self.componentBuilder = componentBuilder
}
/// Build a new instance of the RIB with the given dynamic dependencies.
///
/// - parameter dynamicBuildDependency: The dynamic dependency to use
/// to build the RIB.
/// - parameter dynamicComponentDependency: The dynamic dependency to
/// use to instantiate the component.
/// - returns: The router of the RIB.
public final func build(withDynamicBuildDependency dynamicBuildDependency: DynamicBuildDependency, dynamicComponentDependency: DynamicComponentDependency) -> Router {
return build(withDynamicBuildDependency: dynamicBuildDependency, dynamicComponentDependency: dynamicComponentDependency).1
}
/// Build a new instance of the RIB with the given dynamic dependencies.
///
/// - parameter dynamicBuildDependency: The dynamic dependency to use
/// to build the RIB.
/// - parameter dynamicComponentDependency: The dynamic dependency to
/// use to instantiate the component.
/// - returns: The tuple of component and router of the RIB.
public final func build(withDynamicBuildDependency dynamicBuildDependency: DynamicBuildDependency, dynamicComponentDependency: DynamicComponentDependency) -> (Component, Router) {
let component = componentBuilder(dynamicComponentDependency)
// Ensure each componentBuilder invocation produces a new component
// instance.
let newComponent = component as AnyObject
if lastComponent === newComponent {
assertionFailure("\(self) componentBuilder should produce new instances of component when build is invoked.")
}
lastComponent = newComponent
return (component, build(with: component, dynamicBuildDependency))
}
/// Abstract method that must be overriden to implement the RIB building
/// logic using the given component and dynamic dependency.
///
/// - note: This method should never be invoked directly. Instead
/// consumers of this builder should invoke `build(with dynamicDependency:)`.
/// - parameter component: The corresponding DI component to use.
/// - parameter dynamicBuildDependency: The given dynamic dependency.
/// - returns: The router of the RIB.
open func build(with component: Component, _ dynamicBuildDependency: DynamicBuildDependency) -> Router {
fatalError("This method should be overridden by the subclass.")
}
// MARK: - Private
private let componentBuilder: (DynamicComponentDependency) -> Component
private weak var lastComponent: AnyObject?
}
/// A convenient base builder class that does not require any build or
/// component dynamic dependencies.
///
/// - note: If the build method requires dynamic dependency, please
/// refer to `DynamicBuildComponentizedBuilder`. If component instantiation
/// requires dynamic dependency, please refer to `DynamicComponentizedBuilder`.
/// If both require dynamic dependencies, please use `ComponentizedBuilder`.
/// - SeeAlso: ComponentizedBuilder
open class SimpleComponentizedBuilder<Component, Router>: ComponentizedBuilder<Component, Router, (), ()> {
/// Initializer.
///
/// - parameter componentBuilder: The closure to instantiate a new
/// instance of the DI component that should be paired with this RIB.
#if compiler(>=5.0)
public init(componentBuilder: @escaping () -> Component) {
super.init(componentBuilder: componentBuilder)
}
#else
public override init(componentBuilder: @escaping () -> Component) {
super.init(componentBuilder: componentBuilder)
}
#endif
/// This method should not be directly invoked.
public final override func build(with component: Component, _ dynamicDependency: ()) -> Router {
return build(with: component)
}
/// Abstract method that must be overriden to implement the RIB building
/// logic using the given component.
///
/// - note: This method should never be invoked directly. Instead
/// consumers of this builder should invoke `build(with dynamicDependency:)`.
/// - parameter component: The corresponding DI component to use.
/// - returns: The router of the RIB.
open func build(with component: Component) -> Router {
fatalError("This method should be overriden by the subclass.")
}
/// Build a new instance of the RIB.
///
/// - returns: The router of the RIB.
public final func build() -> Router {
return build(withDynamicBuildDependency: (), dynamicComponentDependency: ())
}
}