From b5e814f628f051be50a51cff7c2dcdb7f8d91946 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:32:57 +0100 Subject: [PATCH 01/24] WIP --- Package.swift | 8 +++-- Package@swift-6.0.swift | 79 ----------------------------------------- 2 files changed, 5 insertions(+), 82 deletions(-) delete mode 100644 Package@swift-6.0.swift diff --git a/Package.swift b/Package.swift index 5c40872..a9db53f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 import PackageDescription @@ -58,7 +58,8 @@ let package = Package( .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), "SwiftUINavigationTransitions", ]), - ] + ], + swiftLanguageModes: [.v5] ) // MARK: Dependencies @@ -72,6 +73,7 @@ package.dependencies = [ for target in package.targets { target.swiftSettings = target.swiftSettings ?? [] target.swiftSettings? += [ - .enableExperimentalFeature("AccessLevelOnImport"), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("InternalImportsByDefault"), ] } diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift deleted file mode 100644 index a9db53f..0000000 --- a/Package@swift-6.0.swift +++ /dev/null @@ -1,79 +0,0 @@ -// swift-tools-version: 6.0 - -import PackageDescription - -let package = Package( - name: "swiftui-navigation-transitions", - platforms: [ - .iOS(.v13), - .macCatalyst(.v13), - .tvOS(.v13), - ], - products: [ - .library(name: "SwiftUINavigationTransitions", targets: ["SwiftUINavigationTransitions"]), - .library(name: "UIKitNavigationTransitions", targets: ["UIKitNavigationTransitions"]), - ], - targets: [ - .target(name: "Animation"), - - .target(name: "Animator"), - .testTarget(name: "AnimatorTests", dependencies: [ - "Animator", - "TestUtils", - ]), - - .target(name: "AtomicTransition", dependencies: [ - "Animator", - ]), - .testTarget(name: "AtomicTransitionTests", dependencies: [ - "AtomicTransition", - "TestUtils", - ]), - - .target(name: "NavigationTransition", dependencies: [ - "Animation", - "AtomicTransition", - .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), - ]), - - .target(name: "UIKitNavigationTransitions", dependencies: [ - "NavigationTransition", - "RuntimeAssociation", - "RuntimeSwizzling", - ]), - - .target(name: "SwiftUINavigationTransitions", dependencies: [ - "NavigationTransition", - "RuntimeAssociation", - "RuntimeSwizzling", - "UIKitNavigationTransitions", - .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), - ]), - - .target(name: "RuntimeAssociation"), - .target(name: "RuntimeSwizzling"), - - .target(name: "TestUtils", dependencies: [ - .product(name: "CustomDump", package: "swift-custom-dump"), - .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), - "SwiftUINavigationTransitions", - ]), - ], - swiftLanguageModes: [.v5] -) - -// MARK: Dependencies - -package.dependencies = [ - .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), // dev - .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"), - .package(url: "https://github.com/siteline/swiftui-introspect", from: "1.0.0"), -] - -for target in package.targets { - target.swiftSettings = target.swiftSettings ?? [] - target.swiftSettings? += [ - .enableUpcomingFeature("ExistentialAny"), - .enableUpcomingFeature("InternalImportsByDefault"), - ] -} From 8b479652fb19ef06d56997366fc17faf3f0f9763 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:39:44 +0100 Subject: [PATCH 02/24] WIP --- Package.resolved | 20 +++++++- Package.swift | 7 +-- Sources/Animation/InterpolatingSpring.swift | 2 +- Sources/Animation/TimingCurve.swift | 2 +- Sources/NavigationTransition/Fade.swift | 2 +- Sources/NavigationTransition/Slide.swift | 2 +- .../RuntimeAssociation.swift | 20 -------- .../RuntimeAssociationPolicy.swift | 29 ----------- Sources/RuntimeSwizzling/Swizzle.swift | 49 ------------------- .../SwiftUISupport.swift | 2 +- .../AnimatorTransientView+Mocks.swift | 2 +- .../UIKitNavigationTransitions/Delegate.swift | 8 +-- .../Interaction.swift | 2 +- .../UIKitSupport.swift | 3 +- 14 files changed, 33 insertions(+), 117 deletions(-) delete mode 100644 Sources/RuntimeAssociation/RuntimeAssociation.swift delete mode 100644 Sources/RuntimeAssociation/RuntimeAssociationPolicy.swift delete mode 100644 Sources/RuntimeSwizzling/Swizzle.swift diff --git a/Package.resolved b/Package.resolved index ac5c811..1a8cb47 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { - "originHash" : "6055238988eaf036fed98cc74283a630a84b8b7c28e6fa1948e8e7d489cf9dbe", + "originHash" : "080f172f15576ef359f4e72c35d09d535df13de1864cd3ff455781f048009756", "pins" : [ + { + "identity" : "objc-runtime-tools", + "kind" : "remoteSourceControl", + "location" : "https://github.com/davdroman/objc-runtime-tools", + "state" : { + "revision" : "c05d245cbb8d289a6bbc184fd2d1605b70ec65f3", + "version" : "0.1.0" + } + }, { "identity" : "swift-custom-dump", "kind" : "remoteSourceControl", @@ -10,6 +19,15 @@ "version" : "1.3.3" } }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" + } + }, { "identity" : "swiftui-introspect", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index a9db53f..c642365 100644 --- a/Package.swift +++ b/Package.swift @@ -38,14 +38,10 @@ let package = Package( .target(name: "UIKitNavigationTransitions", dependencies: [ "NavigationTransition", - "RuntimeAssociation", - "RuntimeSwizzling", + .product(name: "ObjCRuntimeTools", package: "objc-runtime-tools"), ]), .target(name: "SwiftUINavigationTransitions", dependencies: [ - "NavigationTransition", - "RuntimeAssociation", - "RuntimeSwizzling", "UIKitNavigationTransitions", .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), ]), @@ -65,6 +61,7 @@ let package = Package( // MARK: Dependencies package.dependencies = [ + .package(url: "https://github.com/davdroman/objc-runtime-tools", from: "0.1.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), // dev .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"), .package(url: "https://github.com/siteline/swiftui-introspect", from: "1.0.0"), diff --git a/Sources/Animation/InterpolatingSpring.swift b/Sources/Animation/InterpolatingSpring.swift index b41c5b4..1266494 100644 --- a/Sources/Animation/InterpolatingSpring.swift +++ b/Sources/Animation/InterpolatingSpring.swift @@ -1,4 +1,4 @@ -internal import UIKit // TODO: remove internal from all imports when Swift 5.10 is dropped +import UIKit extension Animation { public static func interpolatingSpring( diff --git a/Sources/Animation/TimingCurve.swift b/Sources/Animation/TimingCurve.swift index eb47f68..019adf8 100644 --- a/Sources/Animation/TimingCurve.swift +++ b/Sources/Animation/TimingCurve.swift @@ -1,4 +1,4 @@ -internal import UIKit // TODO: remove internal from all imports when Swift 5.10 is dropped +import UIKit extension Animation { public static func timingCurve( diff --git a/Sources/NavigationTransition/Fade.swift b/Sources/NavigationTransition/Fade.swift index 852d3bd..3796031 100644 --- a/Sources/NavigationTransition/Fade.swift +++ b/Sources/NavigationTransition/Fade.swift @@ -1,4 +1,4 @@ -internal import AtomicTransition +import AtomicTransition extension AnyNavigationTransition { /// A transition that fades the pushed view in, fades the popped view out, or cross-fades both views. diff --git a/Sources/NavigationTransition/Slide.swift b/Sources/NavigationTransition/Slide.swift index a56d07f..a1138df 100644 --- a/Sources/NavigationTransition/Slide.swift +++ b/Sources/NavigationTransition/Slide.swift @@ -1,4 +1,4 @@ -internal import AtomicTransition +import AtomicTransition public import SwiftUI extension AnyNavigationTransition { diff --git a/Sources/RuntimeAssociation/RuntimeAssociation.swift b/Sources/RuntimeAssociation/RuntimeAssociation.swift deleted file mode 100644 index ad4573d..0000000 --- a/Sources/RuntimeAssociation/RuntimeAssociation.swift +++ /dev/null @@ -1,20 +0,0 @@ -import ObjectiveC - -public protocol RuntimeAssociation: AnyObject { - subscript(forKey key: String, policy: RuntimeAssociationPolicy) -> T? { get set } -} - -extension RuntimeAssociation { - public subscript(forKey key: String = #function, policy: RuntimeAssociationPolicy = .retain(.nonatomic)) -> T? { - get { - let key = unsafeBitCast(Selector(key), to: UnsafeRawPointer.self) - return objc_getAssociatedObject(self, key) as? T - } - set { - let key = unsafeBitCast(Selector(key), to: UnsafeRawPointer.self) - objc_setAssociatedObject(self, key, newValue, .init(policy)) - } - } -} - -extension NSObject: RuntimeAssociation {} diff --git a/Sources/RuntimeAssociation/RuntimeAssociationPolicy.swift b/Sources/RuntimeAssociation/RuntimeAssociationPolicy.swift deleted file mode 100644 index dddad10..0000000 --- a/Sources/RuntimeAssociation/RuntimeAssociationPolicy.swift +++ /dev/null @@ -1,29 +0,0 @@ -import ObjectiveC - -public enum RuntimeAssociationPolicy { - public enum Atomicity { - case atomic - case nonatomic - } - - case assign - case copy(Atomicity) - case retain(Atomicity) -} - -extension objc_AssociationPolicy { - init(_ policy: RuntimeAssociationPolicy) { - switch policy { - case .assign: - self = .OBJC_ASSOCIATION_ASSIGN - case .copy(.atomic): - self = .OBJC_ASSOCIATION_COPY - case .copy(.nonatomic): - self = .OBJC_ASSOCIATION_COPY_NONATOMIC - case .retain(.atomic): - self = .OBJC_ASSOCIATION_RETAIN - case .retain(.nonatomic): - self = .OBJC_ASSOCIATION_RETAIN_NONATOMIC - } - } -} diff --git a/Sources/RuntimeSwizzling/Swizzle.swift b/Sources/RuntimeSwizzling/Swizzle.swift deleted file mode 100644 index fec5336..0000000 --- a/Sources/RuntimeSwizzling/Swizzle.swift +++ /dev/null @@ -1,49 +0,0 @@ -public import ObjectiveC - -public var swizzleLogs = false - -public func swizzle(_ type: AnyObject.Type, _ original: Selector, _ swizzled: Selector) { - guard !swizzlingHistory.contains(type, original, swizzled) else { - return - } - - swizzlingHistory.add(type, original, swizzled) - - guard let originalMethod = class_getInstanceMethod(type, original) else { - assertionFailure("[Swizzling] Instance method \(type).\(original) not found.") - return - } - - guard let swizzledMethod = class_getInstanceMethod(type, swizzled) else { - assertionFailure("[Swizzling] Instance method \(type).\(swizzled) not found.") - return - } - - if swizzleLogs { - print("[Swizzling] [\(type) \(original) <~> \(swizzled)]") - } - - method_exchangeImplementations(originalMethod, swizzledMethod) -} - -private struct SwizzlingHistory { - private var map: [Int: Void] = [:] - - func contains(_ type: AnyObject.Type, _ original: Selector, _ swizzled: Selector) -> Bool { - map[hash(type, original, swizzled)] != nil - } - - mutating func add(_ type: AnyObject.Type, _ original: Selector, _ swizzled: Selector) { - map[hash(type, original, swizzled)] = () - } - - private func hash(_ type: AnyObject.Type, _ original: Selector, _ swizzled: Selector) -> Int { - var hasher = Hasher() - hasher.combine(ObjectIdentifier(type)) - hasher.combine(original) - hasher.combine(swizzled) - return hasher.finalize() - } -} - -private var swizzlingHistory = SwizzlingHistory() diff --git a/Sources/SwiftUINavigationTransitions/SwiftUISupport.swift b/Sources/SwiftUINavigationTransitions/SwiftUISupport.swift index eff2f1e..bb4bd89 100644 --- a/Sources/SwiftUINavigationTransitions/SwiftUISupport.swift +++ b/Sources/SwiftUINavigationTransitions/SwiftUISupport.swift @@ -1,6 +1,6 @@ public import UIKitNavigationTransitions public import SwiftUI -@_spi(Advanced) internal import SwiftUIIntrospect +@_spi(Advanced) import SwiftUIIntrospect extension View { @MainActor diff --git a/Sources/TestUtils/AnimatorTransientView+Mocks.swift b/Sources/TestUtils/AnimatorTransientView+Mocks.swift index 5a28d34..353adb2 100644 --- a/Sources/TestUtils/AnimatorTransientView+Mocks.swift +++ b/Sources/TestUtils/AnimatorTransientView+Mocks.swift @@ -1,5 +1,5 @@ @testable public import Animator -internal import UIKit +import UIKit import IssueReporting extension AnimatorTransientView { diff --git a/Sources/UIKitNavigationTransitions/Delegate.swift b/Sources/UIKitNavigationTransitions/Delegate.swift index 66adc3a..d484453 100644 --- a/Sources/UIKitNavigationTransitions/Delegate.swift +++ b/Sources/UIKitNavigationTransitions/Delegate.swift @@ -1,7 +1,7 @@ -internal import Animation -internal import Animator -internal import NavigationTransition -internal import UIKit +import Animation +import Animator +import NavigationTransition +import UIKit final class NavigationTransitionDelegate: NSObject, UINavigationControllerDelegate { var transition: AnyNavigationTransition diff --git a/Sources/UIKitNavigationTransitions/Interaction.swift b/Sources/UIKitNavigationTransitions/Interaction.swift index 9c93001..84cd171 100644 --- a/Sources/UIKitNavigationTransitions/Interaction.swift +++ b/Sources/UIKitNavigationTransitions/Interaction.swift @@ -1,4 +1,4 @@ -internal import UIKit +import UIKit extension UINavigationController { @objc func handleInteraction(_ gestureRecognizer: UIPanGestureRecognizer) { diff --git a/Sources/UIKitNavigationTransitions/UIKitSupport.swift b/Sources/UIKitNavigationTransitions/UIKitSupport.swift index 6eced62..8e39344 100644 --- a/Sources/UIKitNavigationTransitions/UIKitSupport.swift +++ b/Sources/UIKitNavigationTransitions/UIKitSupport.swift @@ -1,6 +1,5 @@ public import NavigationTransition -import RuntimeAssociation -import RuntimeSwizzling +import ObjCRuntimeTools public import UIKit public struct UISplitViewControllerColumns: OptionSet { From 5fe9f2595722a1e4c7b4163928e4a537352f1ae9 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:42:00 +0100 Subject: [PATCH 03/24] WIP --- .../UIKitSupport.swift | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/Sources/UIKitNavigationTransitions/UIKitSupport.swift b/Sources/UIKitNavigationTransitions/UIKitSupport.swift index 8e39344..8bde112 100644 --- a/Sources/UIKitNavigationTransitions/UIKitSupport.swift +++ b/Sources/UIKitNavigationTransitions/UIKitSupport.swift @@ -104,16 +104,13 @@ extension RandomAccessCollection where Index == Int { } extension UINavigationController { - private var defaultDelegate: (any UINavigationControllerDelegate)! { - get { self[] } - set { self[] = newValue } - } + @Associated(.retain(.nonatomic)) + private var defaultDelegate: (any UINavigationControllerDelegate)! - var customDelegate: NavigationTransitionDelegate! { - get { self[] } - set { - self[] = newValue - delegate = newValue + @Associated(.retain(.nonatomic)) + var customDelegate: NavigationTransitionDelegate? { + didSet { + delegate = customDelegate } } @@ -125,10 +122,10 @@ extension UINavigationController { defaultDelegate = delegate } - if customDelegate == nil { - customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate) - } else { + if let customDelegate { customDelegate.transition = transition + } else { + customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate) } swizzle( @@ -272,29 +269,22 @@ extension UINavigationController { interactivePopGestureRecognizer as? UIScreenEdgePanGestureRecognizer } - var defaultPanRecognizer: UIPanGestureRecognizer! { - get { self[] } - set { self[] = newValue } - } + @Associated(.retain(.nonatomic)) + var defaultPanRecognizer: UIPanGestureRecognizer! - var edgePanRecognizer: UIScreenEdgePanGestureRecognizer! { - get { self[] } - set { self[] = newValue } - } + @Associated(.retain(.nonatomic)) + var edgePanRecognizer: UIScreenEdgePanGestureRecognizer! - var panRecognizer: UIPanGestureRecognizer! { - get { self[] } - set { self[] = newValue } - } + @Associated(.retain(.nonatomic)) + var panRecognizer: UIPanGestureRecognizer! } @available(tvOS, unavailable) extension UIGestureRecognizer { + @Associated(.retain(.nonatomic)) var strongDelegate: (any UIGestureRecognizerDelegate)? { - get { self[] } - set { - self[] = newValue - delegate = newValue + didSet { + delegate = strongDelegate } } From b8a12b82710c1d7af541a578cfa3bec21c1b1954 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:58:23 +0100 Subject: [PATCH 04/24] WIP --- Package.resolved | 11 +- Package.swift | 6 +- .../UIKitSupport.swift | 118 +++++++----------- 3 files changed, 60 insertions(+), 75 deletions(-) diff --git a/Package.resolved b/Package.resolved index 1a8cb47..8f339f1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "080f172f15576ef359f4e72c35d09d535df13de1864cd3ff455781f048009756", + "originHash" : "296e08fe1d329d42bb2a4aca5559e7dbe53ae453d7d1be1179f159a52bb9fb2b", "pins" : [ { "identity" : "objc-runtime-tools", @@ -19,6 +19,15 @@ "version" : "1.3.3" } }, + { + "identity" : "swift-once-macro", + "kind" : "remoteSourceControl", + "location" : "https://github.com/davdroman/swift-once-macro", + "state" : { + "revision" : "2bfa91668d16723902cdc558c1a736e8787c91c6", + "version" : "1.0.0" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index c642365..99666de 100644 --- a/Package.swift +++ b/Package.swift @@ -37,8 +37,10 @@ let package = Package( ]), .target(name: "UIKitNavigationTransitions", dependencies: [ + .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), "NavigationTransition", .product(name: "ObjCRuntimeTools", package: "objc-runtime-tools"), + .product(name: "Once", package: "swift-once-macro"), ]), .target(name: "SwiftUINavigationTransitions", dependencies: [ @@ -46,9 +48,6 @@ let package = Package( .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), ]), - .target(name: "RuntimeAssociation"), - .target(name: "RuntimeSwizzling"), - .target(name: "TestUtils", dependencies: [ .product(name: "CustomDump", package: "swift-custom-dump"), .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), @@ -62,6 +61,7 @@ let package = Package( package.dependencies = [ .package(url: "https://github.com/davdroman/objc-runtime-tools", from: "0.1.0"), + .package(url: "https://github.com/davdroman/swift-once-macro", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), // dev .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"), .package(url: "https://github.com/siteline/swiftui-introspect", from: "1.0.0"), diff --git a/Sources/UIKitNavigationTransitions/UIKitSupport.swift b/Sources/UIKitNavigationTransitions/UIKitSupport.swift index 8bde112..b772a01 100644 --- a/Sources/UIKitNavigationTransitions/UIKitSupport.swift +++ b/Sources/UIKitNavigationTransitions/UIKitSupport.swift @@ -1,5 +1,7 @@ +import IssueReporting public import NavigationTransition import ObjCRuntimeTools +import Once public import UIKit public struct UISplitViewControllerColumns: OptionSet { @@ -128,35 +130,51 @@ extension UINavigationController { customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate) } - swizzle( - UINavigationController.self, - #selector(UINavigationController.setViewControllers), - #selector(UINavigationController.setViewControllers_animateIfNeeded) - ) - - swizzle( - UINavigationController.self, - #selector(UINavigationController.pushViewController), - #selector(UINavigationController.pushViewController_animateIfNeeded) - ) - - swizzle( - UINavigationController.self, - #selector(UINavigationController.popViewController), - #selector(UINavigationController.popViewController_animateIfNeeded) - ) - - swizzle( - UINavigationController.self, - #selector(UINavigationController.popToViewController), - #selector(UINavigationController.popToViewController_animateIfNeeded) - ) - - swizzle( - UINavigationController.self, - #selector(UINavigationController.popToRootViewController), - #selector(UINavigationController.popToRootViewController_animateIfNeeded) - ) + do { + try #once { + try #swizzle(UINavigationController.setViewControllers, params: [UIViewController].self, Bool.self) { $self, viewControllers, animated in + if let transitionDelegate = self.customDelegate { + self.setViewControllers(viewControllers, animated: transitionDelegate.transition.animation != nil) + } else { + self.setViewControllers(viewControllers, animated: animated) + } + } + + try #swizzle(UINavigationController.pushViewController, params: UIViewController.self, Bool.self) { $self, viewController, animated in + if let transitionDelegate = self.customDelegate { + self.pushViewController(viewController, animated: transitionDelegate.transition.animation != nil) + } else { + self.pushViewController(viewController, animated: animated) + } + } + + try #swizzle(UINavigationController.popViewController, params: Bool.self, returning: UIViewController?.self) { $self, animated in + if let transitionDelegate = self.customDelegate { + self.popViewController(animated: transitionDelegate.transition.animation != nil) + } else { + self.popViewController(animated: animated) + } + } + + try #swizzle(UINavigationController.popToViewController, params: UIViewController.self, Bool.self, returning: [UIViewController]?.self) { $self, viewController, animated in + if let transitionDelegate = self.customDelegate { + self.popToViewController(viewController, animated: transitionDelegate.transition.animation != nil) + } else { + self.popToViewController(viewController, animated: animated) + } + } + + try #swizzle(UINavigationController.popToRootViewController, params: Bool.self, returning: [UIViewController]?.self) { $self, animated in + if let transitionDelegate = self.customDelegate { + self.popToRootViewController(animated: transitionDelegate.transition.animation != nil) + } else { + self.popToRootViewController(animated: animated) + } + } + } + } catch { + reportIssue(error, "Failed to swizzle required UINavigationController methods") + } #if !os(tvOS) && !os(visionOS) if defaultEdgePanRecognizer.strongDelegate == nil { @@ -220,48 +238,6 @@ extension UINavigationController { } } -extension UINavigationController { - @objc private func setViewControllers_animateIfNeeded(_ viewControllers: [UIViewController], animated: Bool) { - if let transitionDelegate = customDelegate { - setViewControllers_animateIfNeeded(viewControllers, animated: transitionDelegate.transition.animation != nil) - } else { - setViewControllers_animateIfNeeded(viewControllers, animated: animated) - } - } - - @objc private func pushViewController_animateIfNeeded(_ viewController: UIViewController, animated: Bool) { - if let transitionDelegate = customDelegate { - pushViewController_animateIfNeeded(viewController, animated: transitionDelegate.transition.animation != nil) - } else { - pushViewController_animateIfNeeded(viewController, animated: animated) - } - } - - @objc private func popViewController_animateIfNeeded(animated: Bool) -> UIViewController? { - if let transitionDelegate = customDelegate { - popViewController_animateIfNeeded(animated: transitionDelegate.transition.animation != nil) - } else { - popViewController_animateIfNeeded(animated: animated) - } - } - - @objc private func popToViewController_animateIfNeeded(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? { - if let transitionDelegate = customDelegate { - popToViewController_animateIfNeeded(viewController, animated: transitionDelegate.transition.animation != nil) - } else { - popToViewController_animateIfNeeded(viewController, animated: animated) - } - } - - @objc private func popToRootViewController_animateIfNeeded(animated: Bool) -> UIViewController? { - if let transitionDelegate = customDelegate { - popToRootViewController_animateIfNeeded(animated: transitionDelegate.transition.animation != nil) - } else { - popToRootViewController_animateIfNeeded(animated: animated) - } - } -} - @available(tvOS, unavailable) @available(visionOS, unavailable) extension UINavigationController { From df4d7adbb7989a8f87d7a831fc3d40a5512c6482 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:59:20 +0100 Subject: [PATCH 05/24] WIP --- .github/workflows/ci.yml | 1 - .swift-version | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea2a64e..f6e8c4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,6 @@ jobs: - mac-catalyst - tvOS swift: - - "5.10" - "6.0" - "6.1" steps: diff --git a/.swift-version b/.swift-version index f9ce5a9..e0ea36f 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.10 +6.0 From c317e5a68b3aa0c88f2f7a25a2da56b335a922ca Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 12:59:26 +0100 Subject: [PATCH 06/24] WIP --- Sources/TestUtils/AnimatorTransientView+Mocks.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TestUtils/AnimatorTransientView+Mocks.swift b/Sources/TestUtils/AnimatorTransientView+Mocks.swift index 353adb2..b3430b7 100644 --- a/Sources/TestUtils/AnimatorTransientView+Mocks.swift +++ b/Sources/TestUtils/AnimatorTransientView+Mocks.swift @@ -1,6 +1,6 @@ @testable public import Animator -import UIKit import IssueReporting +import UIKit extension AnimatorTransientView { public static var unimplemented: AnimatorTransientView { From 0e7d82b9e69740fe79c7795bcda90f4fbc0aa2f3 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 13:11:50 +0100 Subject: [PATCH 07/24] WIP --- .../UIKitSupport.swift | 104 +++++++++++------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/Sources/UIKitNavigationTransitions/UIKitSupport.swift b/Sources/UIKitNavigationTransitions/UIKitSupport.swift index b772a01..20da4f3 100644 --- a/Sources/UIKitNavigationTransitions/UIKitSupport.swift +++ b/Sources/UIKitNavigationTransitions/UIKitSupport.swift @@ -131,47 +131,7 @@ extension UINavigationController { } do { - try #once { - try #swizzle(UINavigationController.setViewControllers, params: [UIViewController].self, Bool.self) { $self, viewControllers, animated in - if let transitionDelegate = self.customDelegate { - self.setViewControllers(viewControllers, animated: transitionDelegate.transition.animation != nil) - } else { - self.setViewControllers(viewControllers, animated: animated) - } - } - - try #swizzle(UINavigationController.pushViewController, params: UIViewController.self, Bool.self) { $self, viewController, animated in - if let transitionDelegate = self.customDelegate { - self.pushViewController(viewController, animated: transitionDelegate.transition.animation != nil) - } else { - self.pushViewController(viewController, animated: animated) - } - } - - try #swizzle(UINavigationController.popViewController, params: Bool.self, returning: UIViewController?.self) { $self, animated in - if let transitionDelegate = self.customDelegate { - self.popViewController(animated: transitionDelegate.transition.animation != nil) - } else { - self.popViewController(animated: animated) - } - } - - try #swizzle(UINavigationController.popToViewController, params: UIViewController.self, Bool.self, returning: [UIViewController]?.self) { $self, viewController, animated in - if let transitionDelegate = self.customDelegate { - self.popToViewController(viewController, animated: transitionDelegate.transition.animation != nil) - } else { - self.popToViewController(viewController, animated: animated) - } - } - - try #swizzle(UINavigationController.popToRootViewController, params: Bool.self, returning: [UIViewController]?.self) { $self, animated in - if let transitionDelegate = self.customDelegate { - self.popToRootViewController(animated: transitionDelegate.transition.animation != nil) - } else { - self.popToRootViewController(animated: animated) - } - } - } + try UINavigationController.swizzle() } catch { reportIssue(error, "Failed to swizzle required UINavigationController methods") } @@ -225,6 +185,68 @@ extension UINavigationController { #endif } + private static func swizzle() throws { + try #once { + try #swizzle( + UINavigationController.setViewControllers, + params: [UIViewController].self, Bool.self + ) { $self, viewControllers, animated in + if let transitionDelegate = self.customDelegate { + self.setViewControllers(viewControllers, animated: transitionDelegate.transition.animation != nil) + } else { + self.setViewControllers(viewControllers, animated: animated) + } + } + + try #swizzle( + UINavigationController.pushViewController, + params: UIViewController.self, Bool.self + ) { $self, viewController, animated in + if let transitionDelegate = self.customDelegate { + self.pushViewController(viewController, animated: transitionDelegate.transition.animation != nil) + } else { + self.pushViewController(viewController, animated: animated) + } + } + + try #swizzle( + UINavigationController.popViewController, + params: Bool.self, + returning: UIViewController?.self + ) { $self, animated in + if let transitionDelegate = self.customDelegate { + self.popViewController(animated: transitionDelegate.transition.animation != nil) + } else { + self.popViewController(animated: animated) + } + } + + try #swizzle( + UINavigationController.popToViewController, + params: UIViewController.self, Bool.self, + returning: [UIViewController]?.self + ) { $self, viewController, animated in + if let transitionDelegate = self.customDelegate { + self.popToViewController(viewController, animated: transitionDelegate.transition.animation != nil) + } else { + self.popToViewController(viewController, animated: animated) + } + } + + try #swizzle( + UINavigationController.popToRootViewController, + params: Bool.self, + returning: [UIViewController]?.self + ) { $self, animated in + if let transitionDelegate = self.customDelegate { + self.popToRootViewController(animated: transitionDelegate.transition.animation != nil) + } else { + self.popToRootViewController(animated: animated) + } + } + } + } + @available(tvOS, unavailable) @available(visionOS, unavailable) private func exclusivelyEnableGestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer?) { From b554caefac40711ddd50904865a3e4b024cdbc15 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 13:13:21 +0100 Subject: [PATCH 08/24] WIP --- .../UIKitNavigationTransitions/UIKitSupport.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/UIKitNavigationTransitions/UIKitSupport.swift b/Sources/UIKitNavigationTransitions/UIKitSupport.swift index 20da4f3..ffae88e 100644 --- a/Sources/UIKitNavigationTransitions/UIKitSupport.swift +++ b/Sources/UIKitNavigationTransitions/UIKitSupport.swift @@ -120,6 +120,12 @@ extension UINavigationController { _ transition: AnyNavigationTransition, interactivity: AnyNavigationTransition.Interactivity = .default ) { + do { + try UINavigationController.swizzle() + } catch { + reportIssue(error, "Failed to swizzle required UINavigationController methods") + } + if defaultDelegate == nil { defaultDelegate = delegate } @@ -130,12 +136,6 @@ extension UINavigationController { customDelegate = NavigationTransitionDelegate(transition: transition, baseDelegate: defaultDelegate) } - do { - try UINavigationController.swizzle() - } catch { - reportIssue(error, "Failed to swizzle required UINavigationController methods") - } - #if !os(tvOS) && !os(visionOS) if defaultEdgePanRecognizer.strongDelegate == nil { defaultEdgePanRecognizer.strongDelegate = NavigationGestureRecognizerDelegate(controller: self) From 20386957feb8d165ad93288e58bd8afd574be7e4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 12 May 2025 13:16:19 +0100 Subject: [PATCH 09/24] WIP --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6e8c4a..5d455f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,11 @@ jobs: - name: Git Checkout uses: actions/checkout@v4 + - name: Disable Macro Fingerprint Validation + run: | + defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES + defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES + - name: Test Library uses: mxcl/xcodebuild@v3 with: From 4bf4ef60c6dd25a7b1bbc2cbd4c00269ae8b72f0 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:58:31 +0100 Subject: [PATCH 10/24] WIP --- .github/workflows/ci.yml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d455f1..e7524dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ concurrency: jobs: macOS: - name: ${{ matrix.platform }} (Swift ${{ matrix.swift }}) + name: ${{ matrix.platform }} runs-on: macos-15 strategy: fail-fast: false @@ -25,9 +25,6 @@ jobs: - iOS - mac-catalyst - tvOS - swift: - - "6.0" - - "6.1" steps: - name: Git Checkout uses: actions/checkout@v4 @@ -37,11 +34,27 @@ jobs: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES + - if: matrix.platform != 'mac-catalyst' + name: Create Simulators + run: | + set -eo pipefail + xcrun simctl delete all + if [[ "${{ matrix.platform }}" == "iOS" ]]; then + xcrun simctl create "CI iPhone 17 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-17-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-26-0" + elif [[ "${{ matrix.platform }}" == "tvOS" ]]; then + xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" + fi + + - name: List Simulators + run: | + xcrun simctl list + xcodebuild -scheme "SwiftUINavigationTransitions" -showdestinations + - name: Test Library uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ~${{ matrix.swift }} + xcode: ~26.0 action: test workspace: SwiftUINavigationTransitions.xcworkspace scheme: SwiftUINavigationTransitions @@ -50,7 +63,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ~${{ matrix.swift }} + xcode: ~26.0 action: build workspace: SwiftUINavigationTransitions.xcworkspace scheme: Demo From 492f78086b9fa38fea45216aeb714a91d2de2fb1 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:09:32 +0100 Subject: [PATCH 11/24] Revert "WIP" This reverts commit 4bf4ef60c6dd25a7b1bbc2cbd4c00269ae8b72f0. --- .github/workflows/ci.yml | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7524dc..5d455f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ concurrency: jobs: macOS: - name: ${{ matrix.platform }} + name: ${{ matrix.platform }} (Swift ${{ matrix.swift }}) runs-on: macos-15 strategy: fail-fast: false @@ -25,6 +25,9 @@ jobs: - iOS - mac-catalyst - tvOS + swift: + - "6.0" + - "6.1" steps: - name: Git Checkout uses: actions/checkout@v4 @@ -34,27 +37,11 @@ jobs: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES - - if: matrix.platform != 'mac-catalyst' - name: Create Simulators - run: | - set -eo pipefail - xcrun simctl delete all - if [[ "${{ matrix.platform }}" == "iOS" ]]; then - xcrun simctl create "CI iPhone 17 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-17-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-26-0" - elif [[ "${{ matrix.platform }}" == "tvOS" ]]; then - xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" - fi - - - name: List Simulators - run: | - xcrun simctl list - xcodebuild -scheme "SwiftUINavigationTransitions" -showdestinations - - name: Test Library uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - xcode: ~26.0 + swift: ~${{ matrix.swift }} action: test workspace: SwiftUINavigationTransitions.xcworkspace scheme: SwiftUINavigationTransitions @@ -63,7 +50,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - xcode: ~26.0 + swift: ~${{ matrix.swift }} action: build workspace: SwiftUINavigationTransitions.xcworkspace scheme: Demo From dbbb02f2391b027bc81ea6a04d98893492b458c8 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:17:12 +0100 Subject: [PATCH 12/24] WIP --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d455f1..10f198a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,7 @@ jobs: swift: - "6.0" - "6.1" + - "6.2" steps: - name: Git Checkout uses: actions/checkout@v4 @@ -37,6 +38,25 @@ jobs: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES + - if: matrix.platform == 'iOS' || matrix.platform == 'tvOS' + name: Create Simulator + run: | + sudo xcodes select 26.0 + xcrun simctl delete all + if [[ "${{ matrix.swift }}" == "6.2" ]]; then + if [[ "${{ matrix.platform }}" == "iOS" ]]; then + xcrun simctl create "CI iPhone 16 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-26-0" + else + xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" + fi + else + if [[ "${{ matrix.platform }}" == "iOS" ]]; then + xcrun simctl create "CI iPhone 15 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-18-6" + else + xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-18-5" + fi + fi + - name: Test Library uses: mxcl/xcodebuild@v3 with: From ce84593c8ba4b38459569c70e547a684b5e0429b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:21:05 +0100 Subject: [PATCH 13/24] WIP --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10f198a..1c11c33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,13 @@ jobs: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES - - if: matrix.platform == 'iOS' || matrix.platform == 'tvOS' + - if: matrix.swift == '6.2' && matrix.platform != 'mac-catalyst' + name: Select Xcode version + run: | + sudo xcodes select 26.0 + xcodebuild -downloadPlatform ${{ matrix.runtime }} + + - if: matrix.platform != 'mac-catalyst' name: Create Simulator run: | sudo xcodes select 26.0 From a4edbdb614c1f714699adae5bce735781be7af3c Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 18:55:51 +0100 Subject: [PATCH 14/24] WIP --- .github/workflows/ci.yml | 42 ++++++++++++----------- .github/workflows/xcode-simulators.json | 44 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/xcode-simulators.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c11c33..2d63c41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,45 +29,47 @@ jobs: - "6.0" - "6.1" - "6.2" + include: + - swift: "6.0" + xcode: "16.2" + - swift: "6.1" + xcode: "16.4" + - swift: "6.2" + xcode: "26.0" steps: - name: Git Checkout uses: actions/checkout@v4 + - name: Load Simulator Lookup Tables + run: | + echo "SIMULATORS_JSON=$(jq -c . .github/workflows/xcode-simulators.json)" >> $GITHUB_ENV + - name: Disable Macro Fingerprint Validation run: | defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES - - if: matrix.swift == '6.2' && matrix.platform != 'mac-catalyst' - name: Select Xcode version + - if: matrix.platform != 'mac-catalyst' + name: Download Required Runtimes run: | - sudo xcodes select 26.0 - xcodebuild -downloadPlatform ${{ matrix.runtime }} + sudo xcodes select ${{ matrix.xcode }} + xcodebuild -downloadPlatform ${{ matrix.platform }} - if: matrix.platform != 'mac-catalyst' name: Create Simulator run: | - sudo xcodes select 26.0 + sudo xcodes select ${{ matrix.xcode }} xcrun simctl delete all - if [[ "${{ matrix.swift }}" == "6.2" ]]; then - if [[ "${{ matrix.platform }}" == "iOS" ]]; then - xcrun simctl create "CI iPhone 16 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-26-0" - else - xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" - fi - else - if [[ "${{ matrix.platform }}" == "iOS" ]]; then - xcrun simctl create "CI iPhone 15 Pro" "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro" "com.apple.CoreSimulator.SimRuntime.iOS-18-6" - else - xcrun simctl create "CI Apple TV 4K (3rd generation)" "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K" "com.apple.CoreSimulator.SimRuntime.tvOS-18-5" - fi - fi + xcrun simctl create \ + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].name }}" \ + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].type }}" \ + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].runtime }}" - name: Test Library uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ~${{ matrix.swift }} + xcode: ${{ matrix.xcode }} action: test workspace: SwiftUINavigationTransitions.xcworkspace scheme: SwiftUINavigationTransitions @@ -76,7 +78,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ~${{ matrix.swift }} + xcode: ${{ matrix.xcode }} action: build workspace: SwiftUINavigationTransitions.xcworkspace scheme: Demo diff --git a/.github/workflows/xcode-simulators.json b/.github/workflows/xcode-simulators.json new file mode 100644 index 0000000..4a339e3 --- /dev/null +++ b/.github/workflows/xcode-simulators.json @@ -0,0 +1,44 @@ +{ + "16.2": { + "iOS": { + "version": "18.2", + "name": "iPhone 15 Pro", + "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", + "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-18-2" + }, + "tvOS": { + "version": "18.2", + "name": "Apple TV 4K (3rd generation)", + "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", + "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-18-2" + } + }, + "16.4": { + "iOS": { + "version": "18.5", + "name": "iPhone 15 Pro", + "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", + "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-18-5" + }, + "tvOS": { + "version": "18.5", + "name": "Apple TV 4K (3rd generation)", + "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", + "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-18-5" + } + }, + "26.0": { + "iOS": { + "version": "26.0", + "name": "iPhone 16 Pro", + "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro", + "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-26-0" + }, + "tvOS": { + "version": "26.0", + "name": "Apple TV 4K (3rd generation)", + "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", + "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" + } + } +} From 22be610d43c76d313c899e1e5fc536beba58527e Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:01:05 +0100 Subject: [PATCH 15/24] WIP --- .github/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d63c41..308523c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,14 @@ jobs: name: Download Required Runtimes run: | sudo xcodes select ${{ matrix.xcode }} - xcodebuild -downloadPlatform ${{ matrix.platform }} + if ! xcodebuild -downloadPlatform ${{ matrix.platform }}; then + code=$? + if [ $code -ne 70 ]; then + exit $code + else + echo "Ignoring exit code 70 from xcodebuild -downloadPlatform" + fi + fi - if: matrix.platform != 'mac-catalyst' name: Create Simulator From 66f009a412675723c90006aec5a42881c2d89748 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:03:21 +0100 Subject: [PATCH 16/24] WIP --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 308523c..9612737 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,9 +68,9 @@ jobs: sudo xcodes select ${{ matrix.xcode }} xcrun simctl delete all xcrun simctl create \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].name }}" \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].type }}" \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.platform].runtime }}" + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].name }}" \ + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].type }}" \ + "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].runtime }}" - name: Test Library uses: mxcl/xcodebuild@v3 From 502f0312fe653643300bc25d928b189f48ac131f Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:16:32 +0100 Subject: [PATCH 17/24] WIP --- .github/workflows/ci.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9612737..1324f48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,16 +49,25 @@ jobs: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES + - name: Select Xcode version + run: sudo xcodes select ${{ matrix.xcode }} + + - name: List Runtimes & Simulators + run: xcrun simctl list + - if: matrix.platform != 'mac-catalyst' name: Download Required Runtimes run: | sudo xcodes select ${{ matrix.xcode }} - if ! xcodebuild -downloadPlatform ${{ matrix.platform }}; then - code=$? - if [ $code -ne 70 ]; then - exit $code - else - echo "Ignoring exit code 70 from xcodebuild -downloadPlatform" + RUNTIME="${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].runtime }}" + if ! xcrun simctl list runtimes | grep -q "$RUNTIME"; then + if ! xcodebuild -downloadPlatform ${{ matrix.platform }}; then + code=$? + if [ $code -ne 70 ]; then + exit $code + else + echo "Ignoring exit code 70 from xcodebuild -downloadPlatform" + fi fi fi From d8d4bc1eec159758150a139a8e5f5ab94b84de5c Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:31:15 +0100 Subject: [PATCH 18/24] WIP --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1324f48..aaafb38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,8 +52,8 @@ jobs: - name: Select Xcode version run: sudo xcodes select ${{ matrix.xcode }} - - name: List Runtimes & Simulators - run: xcrun simctl list + - name: List Runtimes + run: xcrun simctl list runtimes - if: matrix.platform != 'mac-catalyst' name: Download Required Runtimes From 36d5741eaf36c5c99d9736b195a5a2a27be00342 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:44:29 +0100 Subject: [PATCH 19/24] WIP --- .github/workflows/ci.yml | 40 ++++++---------------------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaafb38..d4e9ef8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,16 +26,9 @@ jobs: - mac-catalyst - tvOS swift: - - "6.0" - - "6.1" - - "6.2" - include: - - swift: "6.0" - xcode: "16.2" - - swift: "6.1" - xcode: "16.4" - - swift: "6.2" - xcode: "26.0" + - ~6.0 # Xcode 16.2 + - ~6.1 # Xcode 16.4 + - ~6.2 # Xcode 26.0 steps: - name: Git Checkout uses: actions/checkout@v4 @@ -58,34 +51,13 @@ jobs: - if: matrix.platform != 'mac-catalyst' name: Download Required Runtimes run: | - sudo xcodes select ${{ matrix.xcode }} - RUNTIME="${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].runtime }}" - if ! xcrun simctl list runtimes | grep -q "$RUNTIME"; then - if ! xcodebuild -downloadPlatform ${{ matrix.platform }}; then - code=$? - if [ $code -ne 70 ]; then - exit $code - else - echo "Ignoring exit code 70 from xcodebuild -downloadPlatform" - fi - fi - fi - - - if: matrix.platform != 'mac-catalyst' - name: Create Simulator - run: | - sudo xcodes select ${{ matrix.xcode }} - xcrun simctl delete all - xcrun simctl create \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].name }}" \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].type }}" \ - "${{ fromJSON(env.SIMULATORS_JSON)[matrix.xcode][matrix.platform].runtime }}" + xcodebuild -downloadPlatform ${{ matrix.platform }} - name: Test Library uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - xcode: ${{ matrix.xcode }} + swift: ${{ matrix.swift }} action: test workspace: SwiftUINavigationTransitions.xcworkspace scheme: SwiftUINavigationTransitions @@ -94,7 +66,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - xcode: ${{ matrix.xcode }} + swift: ${{ matrix.swift }} action: build workspace: SwiftUINavigationTransitions.xcworkspace scheme: Demo From 0f046807a847483dcb3bef188b9984caf6b7d19e Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:44:45 +0100 Subject: [PATCH 20/24] WIP --- .github/workflows/ci.yml | 4 --- .github/workflows/xcode-simulators.json | 44 ------------------------- 2 files changed, 48 deletions(-) delete mode 100644 .github/workflows/xcode-simulators.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4e9ef8..870510a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,10 +33,6 @@ jobs: - name: Git Checkout uses: actions/checkout@v4 - - name: Load Simulator Lookup Tables - run: | - echo "SIMULATORS_JSON=$(jq -c . .github/workflows/xcode-simulators.json)" >> $GITHUB_ENV - - name: Disable Macro Fingerprint Validation run: | defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES diff --git a/.github/workflows/xcode-simulators.json b/.github/workflows/xcode-simulators.json deleted file mode 100644 index 4a339e3..0000000 --- a/.github/workflows/xcode-simulators.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "16.2": { - "iOS": { - "version": "18.2", - "name": "iPhone 15 Pro", - "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", - "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-18-2" - }, - "tvOS": { - "version": "18.2", - "name": "Apple TV 4K (3rd generation)", - "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", - "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-18-2" - } - }, - "16.4": { - "iOS": { - "version": "18.5", - "name": "iPhone 15 Pro", - "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-15-Pro", - "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-18-5" - }, - "tvOS": { - "version": "18.5", - "name": "Apple TV 4K (3rd generation)", - "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", - "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-18-5" - } - }, - "26.0": { - "iOS": { - "version": "26.0", - "name": "iPhone 16 Pro", - "type": "com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro", - "runtime": "com.apple.CoreSimulator.SimRuntime.iOS-26-0" - }, - "tvOS": { - "version": "26.0", - "name": "Apple TV 4K (3rd generation)", - "type": "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K", - "runtime": "com.apple.CoreSimulator.SimRuntime.tvOS-26-0" - } - } -} From 0cfd17b17bf7d28b9c794aabb50736644cc54aea Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:48:03 +0100 Subject: [PATCH 21/24] WIP --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 870510a..7b17a60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,9 @@ jobs: defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES - name: Select Xcode version - run: sudo xcodes select ${{ matrix.xcode }} + uses: mxcl/xcodebuild@v3 + with: + action: none - name: List Runtimes run: xcrun simctl list runtimes From 255fa3889812eb4574bc3ac1330ec8a130008006 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:49:18 +0100 Subject: [PATCH 22/24] WIP --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b17a60..aaff6dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,7 @@ jobs: - name: Select Xcode version uses: mxcl/xcodebuild@v3 with: + swift: ${{ matrix.swift }} action: none - name: List Runtimes From 051792df325770b23d6c1016de02f0ad44183c39 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:51:39 +0100 Subject: [PATCH 23/24] WIP --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaff6dd..07b6106 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: - mac-catalyst - tvOS swift: - - ~6.0 # Xcode 16.2 - - ~6.1 # Xcode 16.4 - - ~6.2 # Xcode 26.0 + - "6.0" # Xcode 16.2 + - "6.1" # Xcode 16.4 + - "6.2" # Xcode 26.0 steps: - name: Git Checkout uses: actions/checkout@v4 @@ -56,7 +56,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ${{ matrix.swift }} + swift: ~${{ matrix.swift }} action: test workspace: SwiftUINavigationTransitions.xcworkspace scheme: SwiftUINavigationTransitions @@ -65,7 +65,7 @@ jobs: uses: mxcl/xcodebuild@v3 with: platform: ${{ matrix.platform }} - swift: ${{ matrix.swift }} + swift: ~${{ matrix.swift }} action: build workspace: SwiftUINavigationTransitions.xcworkspace scheme: Demo From f5b2e25b0eaf13c1b786caaf308cf75ac5378b81 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:57:04 +0100 Subject: [PATCH 24/24] WIP --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07b6106..d1a19f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,7 @@ jobs: - if: matrix.platform != 'mac-catalyst' name: Download Required Runtimes - run: | - xcodebuild -downloadPlatform ${{ matrix.platform }} + run: xcodebuild -downloadPlatform ${{ matrix.platform }} - name: Test Library uses: mxcl/xcodebuild@v3