RIBs/Classes/MultiStageComponentizedBuilder.swift (47 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
/// The base class of a builder that involves multiple stages of building
/// a RIB. Witin the same pass, accesses to the component property shares
/// the same instance. Once `finalStageBuild` is invoked, a new instance
/// is returned from the component property, representing a new pass of
/// the multi-stage building process.
///
/// - SeeAlso: SimpleMultiStageComponentizedBuilder
open class MultiStageComponentizedBuilder<Component, Router, DynamicBuildDependency>: 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.
/// The DI component used for the current iteration of the multi-
/// stage build process. Once `finalStageBuild` method is invoked,
/// this property returns a separate new instance representing a
/// new pass of the multi-stage building process.
public var componentForCurrentBuildPass: Component {
if let currentPassComponent = currentPassComponent {
return currentPassComponent
} else {
let currentPassComponent = componentBuilder()
// Ensure each invocation of componentBuilder produces a new
// component instance.
let newComponent = currentPassComponent as AnyObject
if lastComponent === newComponent {
assertionFailure("\(self) componentBuilder should produce new instances of component when build is invoked.")
}
lastComponent = newComponent
self.currentPassComponent = currentPassComponent
return currentPassComponent
}
}
/// 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 () -> Component) {
self.componentBuilder = componentBuilder
}
/// Build a new instance of the RIB with the given dynamic dependency
/// as the last stage of this mult-stage building process.
///
/// - note: Subsequent access to the `component` property after this
/// method is returned will result in a separate new instance of the
/// component, representing a new pass of the multi-stage building
/// process.
/// - parameter dynamicDependency: The dynamic dependency to use.
/// - returns: The router of the RIB.
public final func finalStageBuild(withDynamicDependency dynamicDependency: DynamicBuildDependency) -> Router {
let router = finalStageBuild(with: componentForCurrentBuildPass, dynamicDependency)
defer {
currentPassComponent = nil
}
return router
}
/// Abstract method that must be overriden to implement the RIB building
/// logic using the given component and dynamic dependency, as the last
/// building stage.
///
/// - note: This method should never be invoked directly. Instead
/// consumers of this builder should invoke `finalStageBuild(with dynamicDependency:)`.
/// - parameter component: The corresponding DI component to use.
/// - parameter dynamicDependency: The given dynamic dependency.
/// - returns: The router of the RIB.
open func finalStageBuild(with component: Component, _ dynamicDependency: DynamicBuildDependency) -> Router {
fatalError("This method should be overridden by the subclass.")
}
// MARK: - Private
private let componentBuilder: () -> Component
private var currentPassComponent: Component?
private weak var lastComponent: AnyObject?
}
/// A convenient base multi-stage builder class that does not require any
/// build dynamic dependencies.
///
/// - note: If the build method requires dynamic dependency, please
/// refer to `MultiStageComponentizedBuilder`.
///
/// - SeeAlso: MultiStageComponentizedBuilder
open class SimpleMultiStageComponentizedBuilder<Component, Router>: MultiStageComponentizedBuilder<Component, Router, ()> {
/// Initializer.
///
/// - parameter componentBuilder: The closure to instantiate a new
/// instance of the DI component that should be paired with this RIB.
public override init(componentBuilder: @escaping () -> Component) {
super.init(componentBuilder: componentBuilder)
}
/// This method should not be directly invoked.
public final override func finalStageBuild(with component: Component, _ dynamicDependency: ()) -> Router {
return finalStageBuild(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 `finalStageBuild()`.
/// - parameter component: The corresponding DI component to use.
/// - returns: The router of the RIB.
open func finalStageBuild(with component: Component) -> Router {
fatalError("This method should be overridden by the subclass.")
}
/// Build a new instance of the RIB as the last stage of this mult-
/// stage building process.
///
/// - note: Subsequent access to the `component` property after this
/// method is returned will result in a separate new instance of the
/// component, representing a new pass of the multi-stage building
/// process.
/// - returns: The router of the RIB.
public final func finalStageBuild() -> Router {
return finalStageBuild(withDynamicDependency: ())
}
}