From cb2c41dccbfa2d5ee68bfeffd72100e1eef84e9a Mon Sep 17 00:00:00 2001 From: Kyle Van Essen Date: Wed, 22 Jun 2022 12:16:42 -0700 Subject: [PATCH] Introduce performInitialUpdate to allow disabling updates during view controller building. --- .../Sources/Screen/ScreenViewController.swift | 9 +++-- .../ViewControllerDescription.swift | 34 +++++++++++++++---- .../ViewControllerDescriptionTests.swift | 18 ++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/WorkflowUI/Sources/Screen/ScreenViewController.swift b/WorkflowUI/Sources/Screen/ScreenViewController.swift index e88f4019a..adfc9ae44 100644 --- a/WorkflowUI/Sources/Screen/ScreenViewController.swift +++ b/WorkflowUI/Sources/Screen/ScreenViewController.swift @@ -71,8 +71,13 @@ /// Convenience to create a view controller description for the given screen /// value. See the example on the comment for ScreenViewController for /// usage. - public final class func description(for screen: ScreenType, environment: ViewEnvironment) -> ViewControllerDescription { - return ViewControllerDescription( + public final class func description( + for screen: ScreenType, + environment: ViewEnvironment, + performInitialUpdate: Bool = true + ) -> ViewControllerDescription { + ViewControllerDescription( + performInitialUpdate: performInitialUpdate, type: self, build: { self.init(screen: screen, environment: environment) }, update: { $0.update(screen: screen, environment: environment) } diff --git a/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift b/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift index f064f4721..936b0070d 100644 --- a/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift +++ b/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift @@ -34,6 +34,15 @@ /// methods such as `buildViewController()`, `update(viewController:)`, if you are /// manually managing your own view controller hierarchy. public struct ViewControllerDescription { + /// If an initial call to `update(viewController:)` will be performed + /// when the view controller is created. Defaults to `true`. + /// + /// ### Note + /// When creating container view controllers that contain other view controllers + /// (eg, a navigation stack), you usually want to set this value to `false` to avoid + /// duplicate updates to your children if they are created in `init`. + public var performInitialUpdate: Bool = true + private let viewControllerType: UIViewController.Type private let build: () -> UIViewController private let checkViewControllerType: (UIViewController) -> Bool @@ -43,13 +52,24 @@ /// build and update a specific view controller type. /// /// - Parameters: + /// - performInitialUpdate: If an initial call to `update(viewController:)` + /// will be performed when the view controller is created. Defaults to `true`. + /// /// - type: The type of view controller produced by this description. - /// Typically, should should be able to omit this parameter, but - /// in cases where type inference has trouble, it’s offered as - /// an escape hatch. + /// Typically, should should be able to omit this parameter, but + /// in cases where type inference has trouble, it’s offered as + /// an escape hatch. + /// /// - build: Closure that produces a new instance of the view controller + /// /// - update: Closure that updates the given view controller - public init(type: VC.Type = VC.self, build: @escaping () -> VC, update: @escaping (VC) -> Void) { + public init( + performInitialUpdate: Bool = true, + type: VC.Type = VC.self, + build: @escaping () -> VC, + update: @escaping (VC) -> Void + ) { + self.performInitialUpdate = performInitialUpdate self.viewControllerType = type self.build = build self.checkViewControllerType = { $0 is VC } @@ -66,8 +86,10 @@ public func buildViewController() -> UIViewController { let viewController = build() - // Perform an initial update of the built view controller - update(viewController: viewController) + if performInitialUpdate { + // Perform an initial update of the built view controller + update(viewController: viewController) + } return viewController } diff --git a/WorkflowUI/Tests/ViewControllerDescriptionTests.swift b/WorkflowUI/Tests/ViewControllerDescriptionTests.swift index f2946e599..1144546e2 100644 --- a/WorkflowUI/Tests/ViewControllerDescriptionTests.swift +++ b/WorkflowUI/Tests/ViewControllerDescriptionTests.swift @@ -82,6 +82,24 @@ XCTAssertFalse(description.canUpdate(viewController: UIViewController())) } + func test_performInitialUpdate() { + var updateCount = 0 + let description = ViewControllerDescription( + performInitialUpdate: false, + build: { BlankViewController() }, + update: { _ in updateCount += 1 } + ) + + XCTAssertEqual(updateCount, 0) + + // Build should not cause an initial update when + let viewController = description.buildViewController() + XCTAssertEqual(updateCount, 0) + + description.update(viewController: viewController) + XCTAssertEqual(updateCount, 1) + } + func test_update() { var updateCount = 0 let description = ViewControllerDescription(