diff --git a/Package.swift b/Package.swift index a978288..3560334 100644 --- a/Package.swift +++ b/Package.swift @@ -10,7 +10,7 @@ let package = Package( ], products: [ .library(name: "UIViewKit", targets: ["UIViewKit"]), -// .library(name: "UIViewKitDevelopmentViews", targets: ["UIViewKitDevelopmentViews"]) + // .library(name: "UIViewKitDevelopmentViews", targets: ["UIViewKitDevelopmentViews"]) ], targets: [ .target( diff --git a/Sources/UIViewKit/UIKitHelpers/UIViewController+Extensions.swift b/Sources/UIViewKit/UIKitHelpers/UIViewController+Extensions.swift index be9dbf5..4a7e1e2 100644 --- a/Sources/UIViewKit/UIKitHelpers/UIViewController+Extensions.swift +++ b/Sources/UIViewKit/UIKitHelpers/UIViewController+Extensions.swift @@ -30,10 +30,10 @@ extension UIViewController { This method ensures that the view controller hierarchy is updated correctly when a child view controller is removed, maintaining the integrity of the view controller lifecycle which is described [here](https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1) - Parameters: - - child: The `UIViewController` instance to be removed from its parent view controller. + - child: The `UIViewController` instance to be removed from its parent view controller. - Important: This method assumes that the `child` view controller is already added to a parent view controller. Calling this method on a view controller that is not a child of any parent will not have any effect. - */ + */ public func ibRemove(child: UIViewController) { child.willMove(toParent: nil) child.view.removeFromSuperview() @@ -46,10 +46,10 @@ extension UIViewController { The removal process ensures a safe and clean detachment of the child view controller's view from the view hierarchy which is described [here](https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1) - Parameters: - - containerView: The UIView container from which the first subview, if it's a child view controller's view, will be removed. + - containerView: The UIView container from which the first subview, if it's a child view controller's view, will be removed. - Important: This method does not perform any action if `containerView` does not have any subviews or if the first subview is not a child view controller's view. - */ + */ public func ibRemove(from containerView: UIView) { guard let childView = containerView.subviews.first else { return diff --git a/Tests/UIViewKitTests/ProTests.swift b/Tests/UIViewKitTests/ProTests.swift index 8edbd5c..e534243 100644 --- a/Tests/UIViewKitTests/ProTests.swift +++ b/Tests/UIViewKitTests/ProTests.swift @@ -15,297 +15,297 @@ class ProTests: XCTestCase { private typealias SUT = UIView func testSingleSut() throws { - let rootView = SUT() - var sut: SUT! - rootView.ibSubviews { - SUT().ibOutlet(&sut) + let rootView = SUT() + var sut: SUT! + rootView.ibSubviews { + SUT().ibOutlet(&sut) + } + XCTAssertEqual(rootView.subviews.count, 1) + XCTAssertNotNil(sut) + XCTAssertEqual(sut.subviews.count, Int.zero) + } + + func testFirstLevel() throws { + let rootView = SUT() + var sut: SUT! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + SUT() + UILabel() + UIImageView() + UITextField() + UITextView() + UIButton() + UIControl() + UIScrollView() + UITableView() + UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) } - XCTAssertEqual(rootView.subviews.count, 1) - XCTAssertNotNil(sut) - XCTAssertEqual(sut.subviews.count, Int.zero) } - func testFirstLevel() throws { - let rootView = SUT() - var sut: SUT! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - SUT() - UILabel() - UIImageView() - UITextField() - UITextView() - UIButton() - UIControl() - UIScrollView() - UITableView() - UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) + XCTAssertTrue(sut.subviews[.zero].isMember(of: SUT.self)) + XCTAssertTrue(sut.subviews[1] is UILabel) + XCTAssertTrue(sut.subviews[2] is UIImageView) + XCTAssertTrue(sut.subviews[3] is UITextField) + XCTAssertTrue(sut.subviews[4] is UITextView) + XCTAssertTrue(sut.subviews[5] is UIButton) + XCTAssertTrue(sut.subviews[6].isMember(of: UIControl.self)) + XCTAssertTrue(sut.subviews[7] is UIScrollView) + XCTAssertTrue(sut.subviews[8] is UITableView) + XCTAssertTrue(sut.subviews[9] is UICollectionView) + } + + func testStackViewWithTwoLabels() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + UILabel().ibOutlet(&label1) + UILabel().ibOutlet(&label2) } } - - XCTAssertTrue(sut.subviews[.zero].isMember(of: SUT.self)) - XCTAssertTrue(sut.subviews[1] is UILabel) - XCTAssertTrue(sut.subviews[2] is UIImageView) - XCTAssertTrue(sut.subviews[3] is UITextField) - XCTAssertTrue(sut.subviews[4] is UITextView) - XCTAssertTrue(sut.subviews[5] is UIButton) - XCTAssertTrue(sut.subviews[6].isMember(of: UIControl.self)) - XCTAssertTrue(sut.subviews[7] is UIScrollView) - XCTAssertTrue(sut.subviews[8] is UITableView) - XCTAssertTrue(sut.subviews[9] is UICollectionView) } - func testStackViewWithTwoLabels() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertTrue(sut.subviews[.zero].subviews[.zero] is UILabel) + XCTAssertTrue(sut.subviews[.zero].subviews[1] is UILabel) + } + + func testStackViewWithinStackViewWithTwoLabels() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var stackViewDetails: UIStackView! + var label1: UILabel! + var label2: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + UIStackView().ibOutlet(&stackViewDetails).ibSubviews { UILabel().ibOutlet(&label1) UILabel().ibOutlet(&label2) } } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertTrue(sut.subviews[.zero].subviews[.zero] is UILabel) - XCTAssertTrue(sut.subviews[.zero].subviews[1] is UILabel) } - func testStackViewWithinStackViewWithTwoLabels() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var stackViewDetails: UIStackView! - var label1: UILabel! - var label2: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - UIStackView().ibOutlet(&stackViewDetails).ibSubviews { - UILabel().ibOutlet(&label1) - UILabel().ibOutlet(&label2) - } - } + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertTrue(sut.subviews[.zero].subviews[.zero] is UIStackView) + XCTAssertTrue(sut.subviews[.zero].subviews[.zero].subviews[.zero] is UILabel) + XCTAssertTrue(sut.subviews[.zero].subviews[.zero].subviews[1] is UILabel) + } + + func testStackViewWithLabelScopedByMacroIf() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { +#if FALSE + UILabel().ibOutlet(&label1) +#endif + UILabel().ibOutlet(&label2) } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertTrue(sut.subviews[.zero].subviews[.zero] is UIStackView) - XCTAssertTrue(sut.subviews[.zero].subviews[.zero].subviews[.zero] is UILabel) - XCTAssertTrue(sut.subviews[.zero].subviews[.zero].subviews[1] is UILabel) } - func testStackViewWithLabelScopedByMacroIf() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - #if FALSE - UILabel().ibOutlet(&label1) - #endif - UILabel().ibOutlet(&label2) - } + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 1) + XCTAssertNil(label1) + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) + } + + func testStackViewWithLabelScopedByMacroIfElse() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + #if FALSE + var label1: UILabel! + #else + var label2: UILabel! + #endif + var label3: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + #if FALSE + UILabel().ibOutlet(&label1) + #else + UILabel().ibOutlet(&label2) + #endif + UILabel().ibOutlet(&label3) } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 1) - XCTAssertNil(label1) - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) } - func testStackViewWithLabelScopedByMacroIfElse() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - #if FALSE - var label1: UILabel! - #else - var label2: UILabel! - #endif - var label3: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - #if FALSE + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) + #if FALSE + XCTAssertNil(label1) + #else + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) + #endif + XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) + } + + func testStackViewWithLabelScopedByIfLet() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + if let _ = Optional([false].randomElement()!) { // swiftlint:disable:this unused_optional_binding UILabel().ibOutlet(&label1) - #else - UILabel().ibOutlet(&label2) - #endif - UILabel().ibOutlet(&label3) } + UILabel().ibOutlet(&label2) } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) - #if FALSE - XCTAssertNil(label1) - #else - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) - #endif - XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) } - func testStackViewWithLabelScopedByIfLet() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - if let _ = Optional([false].randomElement()!) { // swiftlint:disable:this unused_optional_binding - UILabel().ibOutlet(&label1) - } - UILabel().ibOutlet(&label2) + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) + XCTAssertNotNil(label1) + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label1) + XCTAssertEqual(sut.subviews[.zero].subviews[1], label2) + } + + func testStackViewWithLabelScopedByIf() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + if [false].randomElement()! { + UILabel().ibOutlet(&label1) } + UILabel().ibOutlet(&label2) } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) - XCTAssertNotNil(label1) - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label1) - XCTAssertEqual(sut.subviews[.zero].subviews[1], label2) } - func testStackViewWithLabelScopedByIf() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - if [false].randomElement()! { - UILabel().ibOutlet(&label1) - } + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 1) + XCTAssertNil(label1) + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) + } + + func testStackViewWithLabelScopedByIfElse() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + var label3: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + UIStackView().ibOutlet(&stackView).ibSubviews { + if [false].randomElement()! { + UILabel().ibOutlet(&label1) + } else { UILabel().ibOutlet(&label2) } + UILabel().ibOutlet(&label3) } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 1) - XCTAssertNil(label1) - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) } - func testStackViewWithLabelScopedByIfElse() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - var label3: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { - UIStackView().ibOutlet(&stackView).ibSubviews { - if [false].randomElement()! { - UILabel().ibOutlet(&label1) - } else { - UILabel().ibOutlet(&label2) - } - UILabel().ibOutlet(&label3) - } - } - } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) - XCTAssertNil(label1) - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) - XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) - } - - func testExpressionsWhichReturnsVoid() throws { - let rootView = SUT() - var sut: SUT! - var stackView: UIStackView! - var label1: UILabel! - var label2: UILabel! - var label3: UILabel! - - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibSubviews { + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) + XCTAssertNil(label1) + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) + XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) + } + + func testExpressionsWhichReturnsVoid() throws { + let rootView = SUT() + var sut: SUT! + var stackView: UIStackView! + var label1: UILabel! + var label2: UILabel! + var label3: UILabel! + + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibSubviews { + debugPrint() + UIStackView().ibOutlet(&stackView).ibSubviews { debugPrint() - UIStackView().ibOutlet(&stackView).ibSubviews { + if [false].randomElement()! { debugPrint() - if [false].randomElement()! { - debugPrint() - UILabel().ibOutlet(&label1) - debugPrint() - } else { - debugPrint() - UILabel().ibOutlet(&label2) - debugPrint() - } + UILabel().ibOutlet(&label1) + debugPrint() + } else { debugPrint() - UILabel().ibOutlet(&label3) + UILabel().ibOutlet(&label2) debugPrint() } + debugPrint() + UILabel().ibOutlet(&label3) + debugPrint() } } - - XCTAssertTrue(sut.subviews[.zero] is UIStackView) - XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) - XCTAssertNil(label1) - XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) - XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) } - func testSetConstraintsSingle() throws { - let rootView = SUT() + XCTAssertTrue(sut.subviews[.zero] is UIStackView) + XCTAssertEqual(sut.subviews[.zero].subviews.count, 2) + XCTAssertNil(label1) + XCTAssertEqual(sut.subviews[.zero].subviews[.zero], label2) + XCTAssertEqual(sut.subviews[.zero].subviews[1], label3) + } - var sut: SUT! + func testSetConstraintsSingle() throws { + let rootView = SUT() - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibAttributes { - $0.topAnchor.constraint(equalTo: rootView.topAnchor) - } - } + var sut: SUT! - XCTAssertEqual(rootView.constraints.count, 1) - XCTAssertTrue(sut.constraints.isEmpty) - XCTAssertTrue(try XCTUnwrap( UIViewDSLEngine.shared.constraintsToApplyForDebug.isEmpty)) + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibAttributes { + $0.topAnchor.constraint(equalTo: rootView.topAnchor) + } } - func testSetConstraintsMultiple() throws { - let rootView = SUT() + XCTAssertEqual(rootView.constraints.count, 1) + XCTAssertTrue(sut.constraints.isEmpty) + XCTAssertTrue(try XCTUnwrap( UIViewDSLEngine.shared.constraintsToApplyForDebug.isEmpty)) + } - var sut: SUT! + func testSetConstraintsMultiple() throws { + let rootView = SUT() - rootView.ibSubviews { - SUT().ibOutlet(&sut).ibAttributes { - $0.topAnchor.constraint(equalTo: rootView.topAnchor) - $0.leadingAnchor.constraint(equalTo: rootView.leadingAnchor) - $0.trailingAnchor.constraint(equalTo: rootView.trailingAnchor) - $0.bottomAnchor.constraint(equalTo: rootView.bottomAnchor) - } - } + var sut: SUT! - XCTAssertEqual(rootView.constraints.count, 4) - XCTAssertTrue(sut.constraints.isEmpty) - XCTAssertTrue(try XCTUnwrap( UIViewDSLEngine.shared.constraintsToApplyForDebug.isEmpty)) + rootView.ibSubviews { + SUT().ibOutlet(&sut).ibAttributes { + $0.topAnchor.constraint(equalTo: rootView.topAnchor) + $0.leadingAnchor.constraint(equalTo: rootView.leadingAnchor) + $0.trailingAnchor.constraint(equalTo: rootView.trailingAnchor) + $0.bottomAnchor.constraint(equalTo: rootView.bottomAnchor) + } } + + XCTAssertEqual(rootView.constraints.count, 4) + XCTAssertTrue(sut.constraints.isEmpty) + XCTAssertTrue(try XCTUnwrap( UIViewDSLEngine.shared.constraintsToApplyForDebug.isEmpty)) + } } // swiftlint:enable type_body_length