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: ()) } }