Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/validations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ on:
pull_request:

env:
XCODE_VERSION: 16.1
XCODE_VERSION: 26.0.1
TUIST_TEST_DEVICE: iPad (10th generation)
TUIST_TEST_PLATFORM: iOS
TUIST_TEST_OS: 17.2
TUIST_TEST_OS: 18.6

jobs:
development-tests:
Expand Down
16 changes: 6 additions & 10 deletions WorkflowModals/Sources/AnyModalToastContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public final class AnyModalToastContainerViewController: ScreenViewController<An
// the host of our modals once we're in one.
private var needsModalHostUpdate: Bool

// When we're removed from the view (controller) hierarchy, we need to notify our modal host to update our
// modals with an empty modal list. This bool indicates that `aggregateModals` should return an empty array.
private var isBeingRemovedFromHierarchy = false
// This indicates that the controller's view has been removed from its window. When true, modals will
// not be aggregated. However, toasts will continue to aggregate.
private var wasRemovedFromWindow = false

// The manager is lazily instantiated and updated when aggregateModals is first called so that it is more likely to
// use a complete and valid environment during the initial modal list resolution. We may, for example, be in a
Expand All @@ -84,10 +84,6 @@ public final class AnyModalToastContainerViewController: ScreenViewController<An
}

public override func aggregateModals() -> ModalList {
if isBeingRemovedFromHierarchy {
return ModalList(modals: [])
}

// Lazily instantiate and update the manager if needed.
let manager: AnyPresentedModalsManager
if let existingManager = self.manager {
Expand All @@ -108,8 +104,8 @@ public final class AnyModalToastContainerViewController: ScreenViewController<An
}

return ModalList(
modals: aggregateModals.modals
+ presentedModalsAndAggregates.flatMap { [$0] + $1.modals },
modals: wasRemovedFromWindow ? [] : (aggregateModals.modals
+ presentedModalsAndAggregates.flatMap { [$0] + $1.modals }),
toasts: manager.presentedToasts.map { $0.toast }
+ aggregateModals.toasts
+ presentedModalsAndAggregates.flatMap { $1.toasts },
Expand All @@ -132,7 +128,7 @@ public final class AnyModalToastContainerViewController: ScreenViewController<An

public override func loadView() {
view = View(willMoveToWindow: { [weak self] window in
self?.isBeingRemovedFromHierarchy = window == nil
self?.wasRemovedFromWindow = window == nil

if window == nil {
self?.setNeedsModalHostUpdate()
Expand Down
53 changes: 53 additions & 0 deletions WorkflowModals/Tests/ModalHostContainerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,4 +417,57 @@ class ModalHostContainerTests: XCTestCase {
hostContainer.view.layoutIfNeeded()
XCTAssertEqual(hostContainer.preferredContentSize, CGSize(width: axisSize, height: axisSize))
}

func test_aggregation_with_no_window() {
let screen = ModalContainer(
base: ToastContainer(
base: EmptyScreen(),
toasts: [
Toast(
key: "test-toast",
style: ToastPresentationStyleFixture(),
content: EmptyScreen(),
accessibilityAnnouncement: "Test toast."
),
]
),
modals: [
Modal(
key: "test-modal",
style: FullScreenModalStyle(),
content: EmptyScreen()
),
]
)

let viewController = ModalHostContainer.ViewController(
screen: .init(
content: screen,
toastContainerStyle: .fixture
),
environment: .empty
)

show(vc: viewController) { viewController in
viewController.view.layoutIfNeeded()

do {
// When the presenting controller's view is attached to the window, its modals
// and toasts should be aggregated, as long as there is a modal host.
let aggregated = viewController.content.aggregateModals()
XCTAssertEqual(aggregated.modals.count, 1)
XCTAssertEqual(aggregated.toasts.count, 1)
}

do {
// After the presenting controller's view is removed from the window, its modals
// should not be aggregated, but toasts should continue to aggregate.
viewController.view.removeFromSuperview()

let aggregated = viewController.content.aggregateModals()
XCTAssertEqual(aggregated.modals.count, 0)
XCTAssertEqual(aggregated.toasts.count, 1)
}
}
}
}
Loading