From 18715c53135ed518c31fd2590aa13f8785668faa Mon Sep 17 00:00:00 2001 From: Andrew Watt Date: Tue, 1 Jul 2025 18:41:26 -0700 Subject: [PATCH] wip --- ViewEnvironmentUI/README.md | 7 ++-- .../UIView+ViewEnvironmentPropagating.swift | 0 ...ontroller+ViewEnvironmentPropagating.swift | 0 .../ViewEnvironmentObserving.swift | 0 .../ViewEnvironmentPropagating.swift | 0 .../ViewEnvironmentPropagationNode.swift | 0 .../Screen/AdaptedEnvironmentScreen.swift | 0 .../Sources/Screen}/AnyScreen.swift | 9 +---- .../Sources/Screen/Screen.swift | 1 + .../UIViewController+Extensions.swift | 1 + .../ViewControllerDescription.swift | 1 - .../Tests/AdaptedEnvironmentScreenTests.swift | 11 ++++--- .../UIViewControllerExtensionTests.swift | 3 +- .../ViewControllerDescriptionTests.swift | 30 ++--------------- .../Tests/XCTestCase+Extensions.swift | 0 .../AnyScreen+SingleScreenContaining.swift | 25 ++++++++++++++ .../Tests/ScreenViewControllerTests.swift | 33 +++++++++++++++++++ 17 files changed, 76 insertions(+), 45 deletions(-) rename ViewEnvironmentUI/Sources/{ => Propagation}/UIView+ViewEnvironmentPropagating.swift (100%) rename ViewEnvironmentUI/Sources/{ => Propagation}/UIViewController+ViewEnvironmentPropagating.swift (100%) rename ViewEnvironmentUI/Sources/{ => Propagation}/ViewEnvironmentObserving.swift (100%) rename ViewEnvironmentUI/Sources/{ => Propagation}/ViewEnvironmentPropagating.swift (100%) rename ViewEnvironmentUI/Sources/{ => Propagation}/ViewEnvironmentPropagationNode.swift (100%) rename {WorkflowUI => ViewEnvironmentUI}/Sources/Screen/AdaptedEnvironmentScreen.swift (100%) rename {WorkflowUI/Sources/Screen/AnyScreen => ViewEnvironmentUI/Sources/Screen}/AnyScreen.swift (89%) rename {WorkflowUI => ViewEnvironmentUI}/Sources/Screen/Screen.swift (99%) rename {WorkflowUI => ViewEnvironmentUI}/Sources/ViewControllerDescription/UIViewController+Extensions.swift (99%) rename {WorkflowUI => ViewEnvironmentUI}/Sources/ViewControllerDescription/ViewControllerDescription.swift (99%) rename {WorkflowUI => ViewEnvironmentUI}/Tests/AdaptedEnvironmentScreenTests.swift (90%) rename {WorkflowUI => ViewEnvironmentUI}/Tests/UIViewControllerExtensionTests.swift (99%) rename {WorkflowUI => ViewEnvironmentUI}/Tests/ViewControllerDescriptionTests.swift (86%) rename {WorkflowUI => ViewEnvironmentUI}/Tests/XCTestCase+Extensions.swift (100%) create mode 100644 WorkflowUI/Sources/Screen/AnyScreen+SingleScreenContaining.swift create mode 100644 WorkflowUI/Tests/ScreenViewControllerTests.swift diff --git a/ViewEnvironmentUI/README.md b/ViewEnvironmentUI/README.md index a0bbddabb..db2dad618 100644 --- a/ViewEnvironmentUI/README.md +++ b/ViewEnvironmentUI/README.md @@ -1,6 +1,7 @@ # ViewEnvironmentUI -`ViewEnvironmentUI` provides a means to propagate a `ViewEnvironment` through a hierarchy of object nodes. - -Support for propagation of `ViewEnvironment` through `UIViewController`s and `UIView`s is provided by this framework. +`ViewEnvironmentUI` provides some fundamental types to build UI: +- A means to propagate a `ViewEnvironment` through a hierarchy of object nodes, and an implementation of propagation through `UIViewController`s and `UIView`s. +- The `ViewDescription` type, a declarative way to represent a view controller. +- The `Screen` protocol, to create your own types that can produce a `ViewDescription` from a `ViewEnvironment`. diff --git a/ViewEnvironmentUI/Sources/UIView+ViewEnvironmentPropagating.swift b/ViewEnvironmentUI/Sources/Propagation/UIView+ViewEnvironmentPropagating.swift similarity index 100% rename from ViewEnvironmentUI/Sources/UIView+ViewEnvironmentPropagating.swift rename to ViewEnvironmentUI/Sources/Propagation/UIView+ViewEnvironmentPropagating.swift diff --git a/ViewEnvironmentUI/Sources/UIViewController+ViewEnvironmentPropagating.swift b/ViewEnvironmentUI/Sources/Propagation/UIViewController+ViewEnvironmentPropagating.swift similarity index 100% rename from ViewEnvironmentUI/Sources/UIViewController+ViewEnvironmentPropagating.swift rename to ViewEnvironmentUI/Sources/Propagation/UIViewController+ViewEnvironmentPropagating.swift diff --git a/ViewEnvironmentUI/Sources/ViewEnvironmentObserving.swift b/ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentObserving.swift similarity index 100% rename from ViewEnvironmentUI/Sources/ViewEnvironmentObserving.swift rename to ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentObserving.swift diff --git a/ViewEnvironmentUI/Sources/ViewEnvironmentPropagating.swift b/ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentPropagating.swift similarity index 100% rename from ViewEnvironmentUI/Sources/ViewEnvironmentPropagating.swift rename to ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentPropagating.swift diff --git a/ViewEnvironmentUI/Sources/ViewEnvironmentPropagationNode.swift b/ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentPropagationNode.swift similarity index 100% rename from ViewEnvironmentUI/Sources/ViewEnvironmentPropagationNode.swift rename to ViewEnvironmentUI/Sources/Propagation/ViewEnvironmentPropagationNode.swift diff --git a/WorkflowUI/Sources/Screen/AdaptedEnvironmentScreen.swift b/ViewEnvironmentUI/Sources/Screen/AdaptedEnvironmentScreen.swift similarity index 100% rename from WorkflowUI/Sources/Screen/AdaptedEnvironmentScreen.swift rename to ViewEnvironmentUI/Sources/Screen/AdaptedEnvironmentScreen.swift diff --git a/WorkflowUI/Sources/Screen/AnyScreen/AnyScreen.swift b/ViewEnvironmentUI/Sources/Screen/AnyScreen.swift similarity index 89% rename from WorkflowUI/Sources/Screen/AnyScreen/AnyScreen.swift rename to ViewEnvironmentUI/Sources/Screen/AnyScreen.swift index 671a00b0b..556ed6753 100644 --- a/WorkflowUI/Sources/Screen/AnyScreen/AnyScreen.swift +++ b/ViewEnvironmentUI/Sources/Screen/AnyScreen.swift @@ -17,6 +17,7 @@ #if canImport(UIKit) import UIKit +import ViewEnvironment public struct AnyScreen: Screen { /// The original screen, retained for debugging @@ -42,12 +43,4 @@ extension Screen { } } -// MARK: SingleScreenContaining - -extension AnyScreen: SingleScreenContaining { - public var primaryScreen: any Screen { - wrappedScreen - } -} - #endif diff --git a/WorkflowUI/Sources/Screen/Screen.swift b/ViewEnvironmentUI/Sources/Screen/Screen.swift similarity index 99% rename from WorkflowUI/Sources/Screen/Screen.swift rename to ViewEnvironmentUI/Sources/Screen/Screen.swift index a4528410f..f57792eac 100644 --- a/WorkflowUI/Sources/Screen/Screen.swift +++ b/ViewEnvironmentUI/Sources/Screen/Screen.swift @@ -17,6 +17,7 @@ #if canImport(UIKit) import UIKit +import ViewEnvironment /// Screens are the building blocks of an interactive application. /// diff --git a/WorkflowUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift b/ViewEnvironmentUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift similarity index 99% rename from WorkflowUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift rename to ViewEnvironmentUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift index f841c2904..66b3419cb 100644 --- a/WorkflowUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift +++ b/ViewEnvironmentUI/Sources/ViewControllerDescription/UIViewController+Extensions.swift @@ -17,6 +17,7 @@ #if canImport(UIKit) import UIKit +import ViewEnvironment extension UpdateChildScreenViewController where Self: UIViewController { /// Updates the view controller at the given `child` key path with the diff --git a/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift b/ViewEnvironmentUI/Sources/ViewControllerDescription/ViewControllerDescription.swift similarity index 99% rename from WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift rename to ViewEnvironmentUI/Sources/ViewControllerDescription/ViewControllerDescription.swift index ece3684dc..95dc6a59e 100644 --- a/WorkflowUI/Sources/ViewControllerDescription/ViewControllerDescription.swift +++ b/ViewEnvironmentUI/Sources/ViewControllerDescription/ViewControllerDescription.swift @@ -18,7 +18,6 @@ import UIKit import ViewEnvironment -@_spi(ViewEnvironmentWiring) import ViewEnvironmentUI /// A ViewControllerDescription acts as a "recipe" for building and updating a specific `UIViewController`. /// It describes how to _create_ and later _update_ a given view controller instance, without creating one diff --git a/WorkflowUI/Tests/AdaptedEnvironmentScreenTests.swift b/ViewEnvironmentUI/Tests/AdaptedEnvironmentScreenTests.swift similarity index 90% rename from WorkflowUI/Tests/AdaptedEnvironmentScreenTests.swift rename to ViewEnvironmentUI/Tests/AdaptedEnvironmentScreenTests.swift index 91b5c654c..2dee60aa0 100644 --- a/WorkflowUI/Tests/AdaptedEnvironmentScreenTests.swift +++ b/ViewEnvironmentUI/Tests/AdaptedEnvironmentScreenTests.swift @@ -17,8 +17,9 @@ #if canImport(UIKit) import UIKit +import ViewEnvironment +import ViewEnvironmentUI import XCTest -@testable import WorkflowUI class AdaptedEnvironmentScreenTests: XCTestCase { func test_wrapping() { @@ -53,10 +54,12 @@ fileprivate struct TestScreen: Screen { func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription { read(environment) - return ViewController.description(for: self, environment: environment) + return ViewControllerDescription( + environment: environment, + build: UIViewController.init, + update: { _ in } + ) } - - private class ViewController: ScreenViewController {} } #endif diff --git a/WorkflowUI/Tests/UIViewControllerExtensionTests.swift b/ViewEnvironmentUI/Tests/UIViewControllerExtensionTests.swift similarity index 99% rename from WorkflowUI/Tests/UIViewControllerExtensionTests.swift rename to ViewEnvironmentUI/Tests/UIViewControllerExtensionTests.swift index 4417d80d6..f228d01d8 100644 --- a/WorkflowUI/Tests/UIViewControllerExtensionTests.swift +++ b/ViewEnvironmentUI/Tests/UIViewControllerExtensionTests.swift @@ -17,8 +17,9 @@ #if canImport(UIKit) import UIKit +import ViewEnvironment +import ViewEnvironmentUI import XCTest -@testable import WorkflowUI class UIViewControllerExtensionTests: XCTestCase { func test_update_viewNotLoaded() { diff --git a/WorkflowUI/Tests/ViewControllerDescriptionTests.swift b/ViewEnvironmentUI/Tests/ViewControllerDescriptionTests.swift similarity index 86% rename from WorkflowUI/Tests/ViewControllerDescriptionTests.swift rename to ViewEnvironmentUI/Tests/ViewControllerDescriptionTests.swift index dbe289a53..4f3eaac1e 100644 --- a/WorkflowUI/Tests/ViewControllerDescriptionTests.swift +++ b/ViewEnvironmentUI/Tests/ViewControllerDescriptionTests.swift @@ -16,12 +16,10 @@ #if canImport(UIKit) +import ViewEnvironment +import ViewEnvironmentUI import XCTest -import ReactiveSwift -import Workflow -@testable import WorkflowUI - fileprivate class BlankViewController: UIViewController {} @objc fileprivate protocol MyProtocol { @@ -179,30 +177,6 @@ class ViewControllerDescriptionTests: XCTestCase { XCTAssertEqual(environment[TestKey.self], 2) } } - - func test_screenViewController() { - // Make sure ScreenViewController.description(for:) generates a correct view controller - // description - - struct MyScreen: Screen { - func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription { - MyScreenViewController.description(for: self, environment: environment) - } - } - - final class MyScreenViewController: ScreenViewController {} - - let screen = MyScreen() - let description = screen.viewControllerDescription(environment: .empty) - - let viewController = description.buildViewController() - XCTAssertTrue(type(of: viewController) == MyScreenViewController.self) - - XCTAssertTrue(description.canUpdate(viewController: viewController)) - - let viewControllerAgain = description.buildViewController() - XCTAssertFalse(viewController === viewControllerAgain) - } } class ViewControllerDescription_KindIdentifierTests: XCTestCase { diff --git a/WorkflowUI/Tests/XCTestCase+Extensions.swift b/ViewEnvironmentUI/Tests/XCTestCase+Extensions.swift similarity index 100% rename from WorkflowUI/Tests/XCTestCase+Extensions.swift rename to ViewEnvironmentUI/Tests/XCTestCase+Extensions.swift diff --git a/WorkflowUI/Sources/Screen/AnyScreen+SingleScreenContaining.swift b/WorkflowUI/Sources/Screen/AnyScreen+SingleScreenContaining.swift new file mode 100644 index 000000000..375382fa0 --- /dev/null +++ b/WorkflowUI/Sources/Screen/AnyScreen+SingleScreenContaining.swift @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Square Inc. + * + * 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. + */ + +#if canImport(UIKit) + +extension AnyScreen: SingleScreenContaining { + public var primaryScreen: any Screen { + wrappedScreen + } +} + +#endif diff --git a/WorkflowUI/Tests/ScreenViewControllerTests.swift b/WorkflowUI/Tests/ScreenViewControllerTests.swift new file mode 100644 index 000000000..95526aace --- /dev/null +++ b/WorkflowUI/Tests/ScreenViewControllerTests.swift @@ -0,0 +1,33 @@ +#if canImport(UIKit) + +import UIKit +import WorkflowUI +import XCTest + +final class ScreenViewControllerTests: XCTestCase { + func test_screenViewController() { + // Make sure ScreenViewController.description(for:) generates a correct view controller + // description + + struct MyScreen: Screen { + func viewControllerDescription(environment: ViewEnvironment) -> ViewControllerDescription { + MyScreenViewController.description(for: self, environment: environment) + } + } + + final class MyScreenViewController: ScreenViewController {} + + let screen = MyScreen() + let description = screen.viewControllerDescription(environment: .empty) + + let viewController = description.buildViewController() + XCTAssertTrue(type(of: viewController) == MyScreenViewController.self) + + XCTAssertTrue(description.canUpdate(viewController: viewController)) + + let viewControllerAgain = description.buildViewController() + XCTAssertFalse(viewController === viewControllerAgain) + } +} + +#endif