diff --git a/.github/workflows/PackageTests.yml b/.github/workflows/PackageTests.yml new file mode 100644 index 0000000..8b8a229 --- /dev/null +++ b/.github/workflows/PackageTests.yml @@ -0,0 +1,17 @@ +name: PackageTests + +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Run tests + run: xcodebuild test -scheme RPStackable -destination 'platform=iOS Simulator,name=iPhone 12' diff --git a/Example/Stackable.xcodeproj/project.pbxproj b/Example/Stackable.xcodeproj/project.pbxproj index 1ac6997..0f04fc2 100644 --- a/Example/Stackable.xcodeproj/project.pbxproj +++ b/Example/Stackable.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; + B45CA0132756A40000D0AA70 /* InheritanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B45CA0122756A40000D0AA70 /* InheritanceTests.swift */; }; CC72A6DDDA4E7D960CD3D10E /* Pods_Stackable_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0285B0ADF6F8DDE8F69E6BD5 /* Pods_Stackable_Tests.framework */; }; EC5575EECB7557FB0D2EFDA7 /* Pods_Stackable_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 680380F6848D1F3CCB8C984C /* Pods_Stackable_Example.framework */; }; /* End PBXBuildFile section */ @@ -52,6 +53,7 @@ 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 680380F6848D1F3CCB8C984C /* Pods_Stackable_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Stackable_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B32187B246E3626547391D4F /* Pods-Stackable_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Stackable_Example.debug.xcconfig"; path = "Target Support Files/Pods-Stackable_Example/Pods-Stackable_Example.debug.xcconfig"; sourceTree = ""; }; + B45CA0122756A40000D0AA70 /* InheritanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InheritanceTests.swift; sourceTree = ""; }; C0FB5FA864AC1DCB745092FA /* Pods-Stackable_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Stackable_Tests.debug.xcconfig"; path = "Target Support Files/Pods-Stackable_Tests/Pods-Stackable_Tests.debug.xcconfig"; sourceTree = ""; }; C8B792787C4B69CB30904496 /* Pods-Stackable_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Stackable_Example.release.xcconfig"; path = "Target Support Files/Pods-Stackable_Example/Pods-Stackable_Example.release.xcconfig"; sourceTree = ""; }; EA360EB26E10CF4FC56BDD6E /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; @@ -143,6 +145,7 @@ 607FACE81AFB9204008FA782 /* Tests */ = { isa = PBXGroup; children = ( + B45CA0122756A40000D0AA70 /* InheritanceTests.swift */, 607FACEB1AFB9204008FA782 /* Tests.swift */, 00392CDF24EAF2FC001E2F99 /* MemoryTests.swift */, 607FACE91AFB9204008FA782 /* Supporting Files */, @@ -364,6 +367,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B45CA0132756A40000D0AA70 /* InheritanceTests.swift in Sources */, 00392CE024EAF2FC001E2F99 /* MemoryTests.swift in Sources */, 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, ); diff --git a/Example/Tests/InheritanceTests.swift b/Example/Tests/InheritanceTests.swift new file mode 100644 index 0000000..011bf6b --- /dev/null +++ b/Example/Tests/InheritanceTests.swift @@ -0,0 +1,28 @@ +// +// InheritanceTests.swift +// +// +// Created by Jonathan Cole on 11/30/21. +// + +import Stackable +import XCTest + +class InheritingStackView: UIStackView {} + +/** + In these tests, we make sure that classes inheriting from + UIStackView also have full Stackable capabilities applied. + */ +class InheritanceTests: XCTestCase { + + func testDerivingClassHasStackableAddMethod() { + let stack = InheritingStackView() + stack.stackable.add([ + CGFloat(0), + ]) + + XCTAssert(true, "Passes if it compiles.") + } + +} diff --git a/Example/Tests/StackableTests.swift b/Example/Tests/StackableTests.swift new file mode 100644 index 0000000..5d9e284 --- /dev/null +++ b/Example/Tests/StackableTests.swift @@ -0,0 +1,48 @@ +// +// StackableTests.swift +// +// +// Created by Jonathan Cole on 12/1/21. +// + +@testable import Stackable +import XCTest + +class StackableTests: XCTestCase { + + func testHairlineConfigLogic() { + // Test Global config + UIStackView.stackable.hairlineColor = .blue + let stack1 = UIStackView() + stack1.stackable.add([ + UIStackView.stackable.hairline, + ]) + let hairline1 = stack1.arrangedSubviews.first as! StackableHairlineView + XCTAssert(hairline1.backgroundColor == .blue) + + // Test instance config + let stack2 = UIStackView() + stack2.stackable.hairlineColor = .brown + stack2.stackable.add([ + UIStackView.stackable.hairline, + ]) + let hairline2 = stack2.arrangedSubviews.first as! StackableHairlineView + XCTAssert(hairline2.backgroundColor == .brown) + + // Test per-hairline Config + let stack3 = UIStackView() + stack3.stackable.hairlineColor = .brown + stack3.stackable.add([ + UIStackView.stackable.hairline, + UIStackView.stackable.hairline + .color(.yellow), + ]) + let hairline3 = stack3.arrangedSubviews.first as! StackableHairlineView + XCTAssert(hairline3.backgroundColor == .brown) + + let hairline4 = stack3.arrangedSubviews.last as! StackableHairlineView + XCTAssert(hairline4.backgroundColor == .yellow) + + } + +} diff --git a/Package.swift b/Package.swift index 5931a0c..ccc09e6 100644 --- a/Package.swift +++ b/Package.swift @@ -21,5 +21,9 @@ let package = Package( name: "Stackable", dependencies: [], path: "Stackable"), + .testTarget( + name: "StackableTests", + dependencies: ["Stackable"], + path: "Example/Tests"), ] ) diff --git a/Stackable/Stackable+A11y.swift b/Stackable/Stackable+A11y.swift index 474fde1..4e3cd11 100644 --- a/Stackable/Stackable+A11y.swift +++ b/Stackable/Stackable+A11y.swift @@ -21,7 +21,7 @@ internal enum DebugAccessibilityID { public static let margin = "com.rightpoint.stackable.debug.margin" } -public extension StackableExtension where ExtendedType == UIStackView { +public extension StackableExtension where ExtendedType: UIStackView { typealias axID = StackableAccessibilityID diff --git a/Stackable/Stackable+Debug.swift b/Stackable/Stackable+Debug.swift index 9a95d95..aa00bf6 100644 --- a/Stackable/Stackable+Debug.swift +++ b/Stackable/Stackable+Debug.swift @@ -8,7 +8,7 @@ import UIKit -public extension StackableExtension where ExtendedType == UIStackView { +public extension StackableExtension where ExtendedType: UIStackView { /// Debug API extension point var debug: DebugStackableExtension { diff --git a/Stackable/Stackable+Hairlines.swift b/Stackable/Stackable+Hairlines.swift index ceca851..a9dd0cb 100644 --- a/Stackable/Stackable+Hairlines.swift +++ b/Stackable/Stackable+Hairlines.swift @@ -46,7 +46,7 @@ public struct StackableHairline { } // MARK: - Public API -public extension StackableExtension where ExtendedType == UIStackView { +public extension StackableExtension where ExtendedType: UIStackView { /// Add a hairline to the stackView static var hairline: StackableHairline { return .init(type: .next) @@ -419,7 +419,7 @@ public extension UIStackView { } } -public extension StackableExtension where ExtendedType == UIStackView { +public extension StackableExtension where ExtendedType: UIStackView { /// Instance-based `UIStackView` color override. Ultimate styling will prefer hairline-instance config first, but will prefer this over `UIStackView` static config. var hairlineColor: UIColor? { @@ -438,14 +438,23 @@ public extension StackableExtension where ExtendedType == UIStackView { get { return objc_getAssociatedObject(base, &type(of: base).AssociatedKeys.hairlineProvider) as? StackableHairlineProvider } set { objc_setAssociatedObject(base, &type(of: base).AssociatedKeys.hairlineProvider, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } - + /// Static `UIStackView` color override. Ultimate styling will prefer hairline-instance config first, then `UIStackView` instance config. - static var hairlineColor: UIColor? + static var hairlineColor: UIColor? { + get { return objc_getAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineColor) as? UIColor } + set { objc_setAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } /// Static `UIStackView` thickness override. Ultimate styling will prefer hairline-instance config first, then `UIStackView` instance config. - static var hairlineThickness: CGFloat? + static var hairlineThickness: CGFloat? { + get { return objc_getAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineThickness) as? CGFloat } + set { objc_setAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineThickness, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } /// Static `UIStackView` hairlineProvider override. Ultimate styling will prefer `UIStackView` instance config. - static var hairlineProvider: StackableHairlineProvider? + static var hairlineProvider: StackableHairlineProvider? { + get { return objc_getAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineProvider) as? StackableHairlineProvider } + set { objc_setAssociatedObject(self, &UIStackView.AssociatedKeys.hairlineProvider, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } } diff --git a/Stackable/Stackable+Spacing.swift b/Stackable/Stackable+Spacing.swift index 3f08fba..c75ecf1 100644 --- a/Stackable/Stackable+Spacing.swift +++ b/Stackable/Stackable+Spacing.swift @@ -53,7 +53,7 @@ public enum StackableFlexibleSpace { // MARK: - Public API -public extension StackableExtension where ExtendedType == UIStackView { +public extension StackableExtension where ExtendedType: UIStackView { /// Add a space to the stackView. If added after a view, will mirror the visibility of that view. /// - Parameter space: The size of the space. /// - Returns: A `Stackable` that represents the space. diff --git a/Stackable/Stackable.swift b/Stackable/Stackable.swift index d85e8a8..7358cc1 100644 --- a/Stackable/Stackable.swift +++ b/Stackable/Stackable.swift @@ -102,7 +102,7 @@ extension StackableView { // MARK: - UIStackView Public API extension UIView: StackableExtended {} -extension StackableExtension where ExtendedType == UIStackView { +extension StackableExtension where ExtendedType: UIStackView { /** Adds a `Stackable` item to the stackView. diff --git a/Stackable/UIStackView+Utilities.swift b/Stackable/UIStackView+Utilities.swift index 57e3264..4991b53 100644 --- a/Stackable/UIStackView+Utilities.swift +++ b/Stackable/UIStackView+Utilities.swift @@ -9,7 +9,7 @@ import UIKit // MARK: - UIStackView Utilities -extension StackableExtension where ExtendedType == UIStackView { +extension StackableExtension where ExtendedType: UIStackView { /// Removes all `stack.arrangedSubviews` from the `UIStackView` public func removeAllArrangedSubviews() { diff --git a/_Pods.xcodeproj b/_Pods.xcodeproj deleted file mode 120000 index 3c5a8e7..0000000 --- a/_Pods.xcodeproj +++ /dev/null @@ -1 +0,0 @@ -Example/Pods/Pods.xcodeproj \ No newline at end of file