From 5dc99865f3c12c062e0b0042e5c9bd3b49a7e4f8 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 16:23:43 -0500 Subject: [PATCH 01/23] Add poc Contract for HStack --- Package.swift | 5 ++- Sources/SwiftUIKit/Containers/HStack.swift | 48 +++++++++++++++------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index db925b9..e15a42c 100644 --- a/Package.swift +++ b/Package.swift @@ -14,13 +14,16 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), + .package(url: "https://github.com/0xLeif/Later", from: "0.3.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "SwiftUIKit", - dependencies: []), + dependencies: [ + "Later" + ]), .testTarget( name: "SwiftUIKitTests", dependencies: ["SwiftUIKit"]), diff --git a/Sources/SwiftUIKit/Containers/HStack.swift b/Sources/SwiftUIKit/Containers/HStack.swift index 65804b9..bcefdb6 100644 --- a/Sources/SwiftUIKit/Containers/HStack.swift +++ b/Sources/SwiftUIKit/Containers/HStack.swift @@ -6,12 +6,24 @@ // import UIKit +import Later /// Horizontal StackView @available(iOS 9.0, *) public class HStack: UIView { + deinit { + views.resign() + } + + private var spacing: Float + private var padding: Float + private var alignment: UIStackView.Alignment + private var distribution: UIStackView.Distribution /// The views that the HStack contains - public var views: [UIView] = [] + public lazy var views = Contract<[UIView]>(initialValue: []) + .onChange { [weak self] (views) in + self?.draw(views: views ?? []) + } /// Create a HStack /// - Parameters: @@ -25,14 +37,12 @@ public class HStack: UIView { alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, _ closure: () -> [UIView]) { - views = closure() + self.spacing = spacing + self.padding = padding + self.alignment = alignment + self.distribution = distribution super.init(frame: .zero) - - hstack(withSpacing: spacing, - padding: padding, - alignment: alignment, - distribution: distribution, - closure) + views.value = closure() } /// Create a HStack that accepts an array of UIView? @@ -47,18 +57,26 @@ public class HStack: UIView { alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, _ closure: () -> [UIView?]) { - views = closure() - .compactMap { $0 } + self.spacing = spacing + self.padding = padding + self.alignment = alignment + self.distribution = distribution super.init(frame: .zero) + views.value = closure() + .compactMap { $0 } - hstack(withSpacing: spacing, - padding: padding, - alignment: alignment, - distribution: distribution) - { views } } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func draw(views: [UIView]) { + clear() + .hstack(withSpacing: spacing, + padding: padding, + alignment: alignment, + distribution: distribution) + { views } + } } From 8a03d1084bb0817a1913bacf812f91bfe473947a Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 16:27:31 -0500 Subject: [PATCH 02/23] Draw on the main thread --- Sources/SwiftUIKit/Containers/HStack.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftUIKit/Containers/HStack.swift b/Sources/SwiftUIKit/Containers/HStack.swift index bcefdb6..438f3b7 100644 --- a/Sources/SwiftUIKit/Containers/HStack.swift +++ b/Sources/SwiftUIKit/Containers/HStack.swift @@ -22,7 +22,9 @@ public class HStack: UIView { /// The views that the HStack contains public lazy var views = Contract<[UIView]>(initialValue: []) .onChange { [weak self] (views) in - self?.draw(views: views ?? []) + Later.main { + self?.draw(views: views ?? []) + } } /// Create a HStack From 29a6a671e9f5668b0a14416e224af8d22fbef80a Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 16:47:26 -0500 Subject: [PATCH 03/23] Added Contract support for VStack --- Sources/SwiftUIKit/Containers/HStack.swift | 3 +- Sources/SwiftUIKit/Containers/VStack.swift | 53 +++++++++++++++------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Sources/SwiftUIKit/Containers/HStack.swift b/Sources/SwiftUIKit/Containers/HStack.swift index 438f3b7..da0e1f8 100644 --- a/Sources/SwiftUIKit/Containers/HStack.swift +++ b/Sources/SwiftUIKit/Containers/HStack.swift @@ -45,6 +45,7 @@ public class HStack: UIView { self.distribution = distribution super.init(frame: .zero) views.value = closure() + draw(views: views.value ?? []) } /// Create a HStack that accepts an array of UIView? @@ -66,7 +67,7 @@ public class HStack: UIView { super.init(frame: .zero) views.value = closure() .compactMap { $0 } - + draw(views: views.value ?? []) } required init?(coder aDecoder: NSCoder) { diff --git a/Sources/SwiftUIKit/Containers/VStack.swift b/Sources/SwiftUIKit/Containers/VStack.swift index 9ec00d5..81fe577 100644 --- a/Sources/SwiftUIKit/Containers/VStack.swift +++ b/Sources/SwiftUIKit/Containers/VStack.swift @@ -6,12 +6,26 @@ // import UIKit +import Later /// Vertical StackView @available(iOS 9.0, *) public class VStack: UIView { + deinit { + views.resign() + } + + private var spacing: Float + private var padding: Float + private var alignment: UIStackView.Alignment + private var distribution: UIStackView.Distribution /// The views that the VStack contains - public var views: [UIView] = [] + public lazy var views = Contract<[UIView]>() + .onChange { [weak self] (views) in + Later.main { + self?.draw(views: views ?? []) + } + } /// Create a VStack /// - Parameters: @@ -25,14 +39,13 @@ public class VStack: UIView { alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, _ closure: () -> [UIView]) { - views = closure() + self.spacing = spacing + self.padding = padding + self.alignment = alignment + self.distribution = distribution super.init(frame: .zero) - - vstack(withSpacing: spacing, - padding: padding, - alignment: alignment, - distribution: distribution, - closure) + views.value = closure() + draw(views: views.value ?? []) } /// Create a VStack that accepts an array of UIView? @@ -47,18 +60,26 @@ public class VStack: UIView { alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, _ closure: () -> [UIView?]) { - views = closure() - .compactMap { $0 } + self.spacing = spacing + self.padding = padding + self.alignment = alignment + self.distribution = distribution super.init(frame: .zero) - - vstack(withSpacing: spacing, - padding: padding, - alignment: alignment, - distribution: distribution) - { views } + views.value = closure() + .compactMap { $0 } + draw(views: views.value ?? []) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func draw(views: [UIView]) { + clear() + .vstack(withSpacing: spacing, + padding: padding, + alignment: alignment, + distribution: distribution) + { views } + } } From ca77da577ded179c40d0711404029a6c6ca57688 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 17:07:29 -0500 Subject: [PATCH 04/23] Init ContractView --- Sources/SwiftUIKit/Views/ContractView.swift | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Sources/SwiftUIKit/Views/ContractView.swift diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift new file mode 100644 index 0000000..a8a55b9 --- /dev/null +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Zach Eriksen on 8/25/20. +// + +import Foundation From 581e2bf96eac0bc1ba73e0be277993711baa6844 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 17:07:29 -0500 Subject: [PATCH 05/23] Init ContractView --- Sources/SwiftUIKit/Views/ContractView.swift | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Sources/SwiftUIKit/Views/ContractView.swift diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift new file mode 100644 index 0000000..2ed5c09 --- /dev/null +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -0,0 +1,30 @@ +// +// ContractView.swift +// +// +// Created by Zach Eriksen on 8/25/20. +// + +import UIKit +import Later + +@available(iOS 9.0, *) +public class ContractView: UIView where View: UIView, SomeContract: Contract { + public weak var contract: SomeContract? + public let view: View + + public init(view: View, _ closure: (View) -> SomeContract) { + self.view = view + super.init(frame: .zero) + + self.contract = closure(view) + + embed { + view + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} From 4fba7763905284460865ae5d07d47de96a592a88 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 17:19:46 -0500 Subject: [PATCH 06/23] Remove weak ref --- Sources/SwiftUIKit/Views/ContractView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift index 90f6de3..bc2edbd 100644 --- a/Sources/SwiftUIKit/Views/ContractView.swift +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -10,7 +10,7 @@ import Later @available(iOS 9.0, *) public class ContractView: UIView where View: UIView, SomeContract: Contract { - public weak var contract: SomeContract? + public var contract: SomeContract? public init(view: View, _ closure: (View) -> SomeContract) { super.init(frame: .zero) From f43f3c3921770864032a78fbaa9982ef0a9fd0df Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 17:40:23 -0500 Subject: [PATCH 07/23] Reduce generics --- Sources/SwiftUIKit/Views/ContractView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift index bc2edbd..c5705cd 100644 --- a/Sources/SwiftUIKit/Views/ContractView.swift +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -9,10 +9,10 @@ import UIKit import Later @available(iOS 9.0, *) -public class ContractView: UIView where View: UIView, SomeContract: Contract { - public var contract: SomeContract? +public class ContractView: UIView where View: UIView { + public var contract: Contract? - public init(view: View, _ closure: (View) -> SomeContract) { + public init(view: View, _ closure: (View) -> Contract) { super.init(frame: .zero) self.contract = closure(view) From 5916c0b67de52a2552a35ad650136915545f5ac4 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 18:01:57 -0500 Subject: [PATCH 08/23] Added quick force fire on Contract --- Sources/SwiftUIKit/Views/ContractView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift index c5705cd..472d350 100644 --- a/Sources/SwiftUIKit/Views/ContractView.swift +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -20,6 +20,8 @@ public class ContractView: UIView where View: UIView { embed { view } + + contract?.value = contract?.value } required init?(coder: NSCoder) { From b3e6981fa7e2fac2c07e2f69d385f13a0317743f Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Tue, 25 Aug 2020 18:06:55 -0500 Subject: [PATCH 09/23] Update swift.yml --- .github/workflows/swift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 7784c59..ab057e0 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -9,14 +9,14 @@ jobs: strategy: matrix: destination: ['platform=iOS Simulator,OS=13.1,name=iPhone 8'] - xcode: ['/Applications/Xcode_11.1.app/Contents/Developer'] + xcode: ['/Applications/Xcode_11.6.app/Contents/Developer'] steps: - uses: actions/checkout@v1 # Github Actions' machines do in fact have recent versions of Xcode, # but you may have to explicitly switch to them. We explicitly want # to use Xcode 11, so we use xcode-select to switch to it. - name: Switch to Xcode 11 - run: sudo xcode-select --switch /Applications/Xcode_11.1.app + run: sudo xcode-select --switch /Applications/Xcode_11.6.app # Since we want to be running our tests from Xcode, we need to # generate an .xcodeproj file. Luckly, Swift Package Manager has # build in functionality to do so. From 83d73740b19fab9241a3831de535d402077a4d1c Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 18:24:29 -0500 Subject: [PATCH 10/23] Removed extra space --- Sources/SwiftUIKit/Views/ContractView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftUIKit/Views/ContractView.swift b/Sources/SwiftUIKit/Views/ContractView.swift index 472d350..99885af 100644 --- a/Sources/SwiftUIKit/Views/ContractView.swift +++ b/Sources/SwiftUIKit/Views/ContractView.swift @@ -10,7 +10,7 @@ import Later @available(iOS 9.0, *) public class ContractView: UIView where View: UIView { - public var contract: Contract? + public var contract: Contract? public init(view: View, _ closure: (View) -> Contract) { super.init(frame: .zero) From fb02c1bf6579686ac69fd559098728e8ed63be81 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:38:43 -0500 Subject: [PATCH 11/23] Removed Linux test references --- Tests/LinuxMain.swift | 7 ------- .../NSMutableAttributedStringTests.swift | 5 ----- Tests/SwiftUIKitTests/XCTestManifests.swift | 11 ----------- 3 files changed, 23 deletions(-) delete mode 100644 Tests/LinuxMain.swift delete mode 100644 Tests/SwiftUIKitTests/XCTestManifests.swift diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 714cbb4..0000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest - -import SwiftUIKitTests - -var tests = [XCTestCaseEntry]() -tests += SwiftUIKitTests.allTests() -XCTMain(tests) diff --git a/Tests/SwiftUIKitTests/NSAttributedString/NSMutableAttributedStringTests.swift b/Tests/SwiftUIKitTests/NSAttributedString/NSMutableAttributedStringTests.swift index 4dcb440..2e76072 100644 --- a/Tests/SwiftUIKitTests/NSAttributedString/NSMutableAttributedStringTests.swift +++ b/Tests/SwiftUIKitTests/NSAttributedString/NSMutableAttributedStringTests.swift @@ -74,9 +74,4 @@ final class NSMutableAttributedStringTests: XCTestCase { XCTAssert(!(applyLabel.text?.isEmpty ?? true)) XCTAssert(!(applyLabel.accessibilityLabel?.isEmpty ?? true)) } - - static var allTests = [ - ("testAttributedString", testAttributedString), - ("testApplyLabel", testApplyLabel) - ] } diff --git a/Tests/SwiftUIKitTests/XCTestManifests.swift b/Tests/SwiftUIKitTests/XCTestManifests.swift deleted file mode 100644 index 30c9364..0000000 --- a/Tests/SwiftUIKitTests/XCTestManifests.swift +++ /dev/null @@ -1,11 +0,0 @@ -import XCTest - -#if !canImport(ObjectiveC) -public func allTests() -> [XCTestCaseEntry] { - return [ - testCase(BasicSwiftUIKitTests.allTests), - testCase(BasicADATests.allTests), - testCase(NSMutableAttributedStringTests.allTests), - ] -} -#endif From 97ba2478f22cefe4a68daa1d6f70fdcb87b0ce2c Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:39:46 -0500 Subject: [PATCH 12/23] Added tests H/V Stack; Switched to XCTAssertEqual --- .../Core/BasicSwiftUIKitTests.swift | 188 ++++++++++++------ 1 file changed, 126 insertions(+), 62 deletions(-) diff --git a/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift b/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift index 655dc4f..08a9e75 100644 --- a/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift +++ b/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift @@ -7,6 +7,7 @@ import Foundation import XCTest +import Later @testable import SwiftUIKit @available(iOS 10.0, *) @@ -17,8 +18,8 @@ final class BasicSwiftUIKitTests: XCTestCase { let view = UIView() XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 0) - XCTAssert(view.constraints.count == 0) + XCTAssertEqual(view.allSubviews.count, 0) + XCTAssertEqual(view.constraints.count, 0) } func testEmbedView() { @@ -37,8 +38,8 @@ final class BasicSwiftUIKitTests: XCTestCase { let topConstraint = view.topConstraints.first XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 1) - XCTAssert(view.constraints.count == 4) + XCTAssertEqual(view.allSubviews.count, 1) + XCTAssertEqual(view.constraints.count, 4) XCTAssertEqual(leadingConstraint?.constant, 0) XCTAssertEqual(bottomConstraint?.constant, 0) XCTAssertEqual(trailingConstraint?.constant, 0) @@ -46,7 +47,7 @@ final class BasicSwiftUIKitTests: XCTestCase { view.update(padding: 16) - XCTAssert(view.constraints.count == 4) + XCTAssertEqual(view.constraints.count, 4) XCTAssertEqual(leadingConstraint?.constant, 16) XCTAssertEqual(bottomConstraint?.constant, -16) XCTAssertEqual(trailingConstraint?.constant, -16) @@ -68,8 +69,8 @@ final class BasicSwiftUIKitTests: XCTestCase { let constraint = view.leadingConstraints.first XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 1) - XCTAssert(view.constraints.count == 1) + XCTAssertEqual(view.allSubviews.count, 1) + XCTAssertEqual(view.constraints.count, 1) XCTAssertEqual(constraint?.constant, 16) view.update(padding: .leading(8)) @@ -96,15 +97,15 @@ final class BasicSwiftUIKitTests: XCTestCase { let bottomConstraint = view.bottomConstraints.first XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 1) - XCTAssert(view.constraints.count == 2) + XCTAssertEqual(view.allSubviews.count, 1) + XCTAssertEqual(view.constraints.count, 2) XCTAssertEqual(leadingConstraint?.constant, 16) XCTAssertEqual(bottomConstraint?.constant, -16) view.update(padding: .leading(8)) view.update(padding: .bottom(32)) - XCTAssert(view.constraints.count == 2) + XCTAssertEqual(view.constraints.count, 2) XCTAssertEqual(leadingConstraint?.constant, 8) XCTAssertEqual(bottomConstraint?.constant, -32) } @@ -128,15 +129,15 @@ final class BasicSwiftUIKitTests: XCTestCase { let trailingConstraint = view.trailingConstraints.first XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 1) - XCTAssert(view.constraints.count == 3) + XCTAssertEqual(view.allSubviews.count, 1) + XCTAssertEqual(view.constraints.count, 3) XCTAssertEqual(leadingConstraint?.constant, 16) XCTAssertEqual(bottomConstraint?.constant, -16) XCTAssertEqual(trailingConstraint?.constant, -16) view.update(padding: [.leading(32), .trailing(32), .bottom(32)]) - XCTAssert(view.constraints.count == 3) + XCTAssertEqual(view.constraints.count, 3) XCTAssertEqual(leadingConstraint?.constant, 32) XCTAssertEqual(bottomConstraint?.constant, -32) XCTAssertEqual(trailingConstraint?.constant, -32) @@ -163,8 +164,8 @@ final class BasicSwiftUIKitTests: XCTestCase { let topConstraint = view.topConstraints.first XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 1) - XCTAssert(view.constraints.count == 4) + XCTAssertEqual(view.allSubviews.count, 1) + XCTAssertEqual(view.constraints.count, 4) XCTAssertEqual(leadingConstraint?.constant, 16) XCTAssertEqual(bottomConstraint?.constant, -16) XCTAssertEqual(trailingConstraint?.constant, -16) @@ -172,7 +173,7 @@ final class BasicSwiftUIKitTests: XCTestCase { view.update(padding: 32) - XCTAssert(view.constraints.count == 4) + XCTAssertEqual(view.constraints.count, 4) XCTAssertEqual(leadingConstraint?.constant, 32) XCTAssertEqual(bottomConstraint?.constant, -32) XCTAssertEqual(trailingConstraint?.constant, -32) @@ -192,8 +193,8 @@ final class BasicSwiftUIKitTests: XCTestCase { } XCTAssertNil(view.backgroundColor) - XCTAssert(view.allSubviews.count == 2) - XCTAssert(view.constraints.count == 4) + XCTAssertEqual(view.allSubviews.count, 2) + XCTAssertEqual(view.constraints.count, 4) } func testVStackView() { @@ -207,7 +208,46 @@ final class BasicSwiftUIKitTests: XCTestCase { } XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) - XCTAssert(stack.allSubviews.count == 2) + XCTAssertEqual(stack.allSubviews.count, 2) + XCTAssertEqual(stack.views.value?.count, 1) + } + + func testVStackViewAppend_one() { + + let viewToEmbed = UIView() + + let stack = VStack { + [ + viewToEmbed + ] + } + + stack.views.value?.append(UIView()) + + stack.draw(views: stack.views.value ?? []) + + XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) + XCTAssertEqual(stack.allSubviews.count, 3) + XCTAssertEqual(stack.views.value?.count, 2) + } + + func testVStackViewAppend_five() { + + let viewToEmbed = UIView() + + let stack = VStack { + [ + viewToEmbed + ] + } + + stack.views.value? += (0 ... 4).map { Label("\($0)") } + + stack.draw(views: stack.views.value ?? []) + + XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) + XCTAssertEqual(stack.allSubviews.count, 7) + XCTAssertEqual(stack.views.value?.count, 6) } func testHStackView() { @@ -221,7 +261,45 @@ final class BasicSwiftUIKitTests: XCTestCase { } XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) - XCTAssert(stack.allSubviews.count == 2) + XCTAssertEqual(stack.allSubviews.count, 2) + XCTAssertEqual(stack.views.value?.count, 1) + } + + func testHStackViewAppend_one() { + + let viewToEmbed = UIView() + + let stack = VStack { + [ + viewToEmbed + ] + } + + stack.views.value?.append(UIView()) + + stack.draw(views: stack.views.value ?? []) + + XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) + XCTAssertEqual(stack.allSubviews.count, 3) + XCTAssertEqual(stack.views.value?.count, 2) + } + + func testHStackViewAppend_five() { + let viewToEmbed = UIView() + + let stack = VStack { + [ + viewToEmbed + ] + } + + stack.views.value? += (0 ... 4).map { Label("\($0)") } + + stack.draw(views: stack.views.value ?? []) + + XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) + XCTAssertEqual(stack.allSubviews.count, 7) + XCTAssertEqual(stack.views.value?.count, 6) } func testZStackView() { @@ -234,14 +312,14 @@ final class BasicSwiftUIKitTests: XCTestCase { ] } - XCTAssert(stack.allSubviews.count == 1) + XCTAssertEqual(stack.allSubviews.count, 1) } func testPaddingView() { let view = UIView().padding() - XCTAssert(view.allSubviews.count == 1) + XCTAssertEqual(view.allSubviews.count, 1) } func testConfigureView() { @@ -258,14 +336,14 @@ final class BasicSwiftUIKitTests: XCTestCase { .hide(if: true) .clipsToBounds(true) - XCTAssert(view.backgroundColor == .blue) - XCTAssert(view.isHidden == true) - XCTAssert(view.tintColor == .green) - XCTAssert(view.clipsToBounds == true) + XCTAssertEqual(view.backgroundColor, .blue) + XCTAssertEqual(view.isHidden, true) + XCTAssertEqual(view.tintColor, .green) + XCTAssertEqual(view.clipsToBounds, true) - XCTAssert(view.backgroundColor == otherView.backgroundColor) - XCTAssert(view.isHidden == otherView.isHidden) - XCTAssert(view.clipsToBounds == otherView.clipsToBounds) + XCTAssertEqual(view.backgroundColor, otherView.backgroundColor) + XCTAssertEqual(view.isHidden, otherView.isHidden) + XCTAssertEqual(view.clipsToBounds, otherView.clipsToBounds) } func testLayerView() { @@ -281,12 +359,12 @@ final class BasicSwiftUIKitTests: XCTestCase { let otherView = UIView() .layer(cornerRadius: 8) - XCTAssert(view.layer.borderColor == UIColor.blue.cgColor) - XCTAssert(view.layer.borderWidth == 3) - XCTAssert(view.layer.cornerRadius == 8) - XCTAssert(view.layer.masksToBounds == true) + XCTAssertEqual(view.layer.borderColor, UIColor.blue.cgColor) + XCTAssertEqual(view.layer.borderWidth, 3) + XCTAssertEqual(view.layer.cornerRadius, 8) + XCTAssertEqual(view.layer.masksToBounds, true) - XCTAssert(view.layer.cornerRadius == otherView.layer.cornerRadius) + XCTAssertEqual(view.layer.cornerRadius, otherView.layer.cornerRadius) } func testClearView() { @@ -321,14 +399,14 @@ final class BasicSwiftUIKitTests: XCTestCase { } // Will fail unless... - // === (iOS >= 13) === - XCTAssert(switchView.allSubviews.count == 8, "switchView.allSubviews.count == \(switchView.allSubviews.count)") - XCTAssert(uiSwitchView.allSubviews.count == 8, "uiSwitchView.allSubviews.count == \(uiSwitchView.allSubviews.count)") - XCTAssert(view.allSubviews.count == 12, "view.allSubviews.count == \(view.allSubviews.count)") - XCTAssert(otherView.allSubviews.count == 12, "otherView.allSubviews.count == \(otherView.allSubviews.count)") - // === (End) === + //,= (iOS >= 13),= + XCTAssertEqual(switchView.allSubviews.count, 8) + XCTAssertEqual(uiSwitchView.allSubviews.count, 8) + XCTAssertEqual(view.allSubviews.count, 12) + XCTAssertEqual(otherView.allSubviews.count, 12) + //,= (End),= - XCTAssert(viewWithoutSwitch.allSubviews.count == 3, "viewWithoutSwitch.allSubviews.count == \(viewWithoutSwitch.allSubviews.count)") + XCTAssertEqual(viewWithoutSwitch.allSubviews.count, 3) switchView.clear() uiSwitchView.clear() @@ -337,12 +415,12 @@ final class BasicSwiftUIKitTests: XCTestCase { otherView.clear() viewWithoutSwitch.clear() - XCTAssert(switchView.allSubviews.count == 0) - XCTAssert(uiSwitchView.allSubviews.count == 0) + XCTAssertEqual(switchView.allSubviews.count, 0) + XCTAssertEqual(uiSwitchView.allSubviews.count, 0) - XCTAssert(view.allSubviews.count == 0) - XCTAssert(otherView.allSubviews.count == 0) - XCTAssert(viewWithoutSwitch.allSubviews.count == 0) + XCTAssertEqual(view.allSubviews.count, 0) + XCTAssertEqual(otherView.allSubviews.count, 0) + XCTAssertEqual(viewWithoutSwitch.allSubviews.count, 0) } func testLayoutConstraint() { @@ -363,21 +441,7 @@ final class BasicSwiftUIKitTests: XCTestCase { ] } - XCTAssert(innerView.constraints.count == 2) - XCTAssert(stack.constraints.count == 2) + XCTAssertEqual(innerView.constraints.count, 2) + XCTAssertEqual(stack.constraints.count, 2) } - - static var allTests = [ - ("testDefaultView", testDefaultView), - ("testEmbedViews", testEmbedViews), - ("testEmbedView", testEmbedView), - ("testVStackView", testVStackView), - ("testHStackView", testHStackView), - ("testZStackView", testZStackView), - ("testPaddingView", testPaddingView), - ("testConfigureView", testConfigureView), - ("testLayerView", testLayerView), - ("testClearView", testClearView), - ("testLayoutConstraint", testLayoutConstraint) - ] } From 7b8cd59d7f9ad4d339e1e6fe377f52146a005b79 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:40:10 -0500 Subject: [PATCH 13/23] Switched to XCTAssertEqual --- Tests/SwiftUIKitTests/ADA/BasicADATests.swift | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/Tests/SwiftUIKitTests/ADA/BasicADATests.swift b/Tests/SwiftUIKitTests/ADA/BasicADATests.swift index 4eadd75..02c55f4 100644 --- a/Tests/SwiftUIKitTests/ADA/BasicADATests.swift +++ b/Tests/SwiftUIKitTests/ADA/BasicADATests.swift @@ -20,18 +20,18 @@ final class BasicADATests: XCTestCase { .padding() .debug() - assert(label.accessibilityLabel == "SomeString") - assert(label.accessibilityIdentifier == "SomeID") - assert(label.accessibilityTraits == .staticText) + XCTAssertEqual(label.accessibilityLabel, "SomeString") + XCTAssertEqual(label.accessibilityIdentifier, "SomeID") + XCTAssertEqual(label.accessibilityTraits, .staticText) } func testButtonADA() { let button = Button("SomeString") { print("Hello") } .accessibility(label: nil) - assert(button.accessibilityLabel == "SomeString") - assert(button.accessibilityIdentifier == nil) - assert(button.accessibilityTraits == .button) + XCTAssertEqual(button.accessibilityLabel, "SomeString") + XCTAssertEqual(button.accessibilityIdentifier, nil) + XCTAssertEqual(button.accessibilityTraits, .button) } func testComplexViewADA() { @@ -49,15 +49,9 @@ final class BasicADATests: XCTestCase { let accessibilityLabels = ["Hello World", "Ipsum"] - assert(view.allSubviews.compactMap { $0.accessibilityLabel } == accessibilityLabels) - assert(view.accessibilityIdentifier == "mainView") - assert(view.accessibilityTraits == .none) - assert(view.shouldGroupAccessibilityChildren == false) + XCTAssertEqual(view.allSubviews.compactMap { $0.accessibilityLabel }, accessibilityLabels) + XCTAssertEqual(view.accessibilityIdentifier, "mainView") + XCTAssertEqual(view.accessibilityTraits, .none) + XCTAssertEqual(view.shouldGroupAccessibilityChildren, false) } - - static var allTests = [ - ("testLabelADA", testLabelADA), - ("testButtonADA", testButtonADA), - ("testComplexViewADA", testComplexViewADA) - ] } From 1a45c726fc5fef779e2f17c516547692b64799c7 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:40:22 -0500 Subject: [PATCH 14/23] Allow for draw in Tests --- Sources/SwiftUIKit/Containers/HStack.swift | 2 +- Sources/SwiftUIKit/Containers/VStack.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftUIKit/Containers/HStack.swift b/Sources/SwiftUIKit/Containers/HStack.swift index da0e1f8..d887537 100644 --- a/Sources/SwiftUIKit/Containers/HStack.swift +++ b/Sources/SwiftUIKit/Containers/HStack.swift @@ -74,7 +74,7 @@ public class HStack: UIView { fatalError("init(coder:) has not been implemented") } - private func draw(views: [UIView]) { + internal func draw(views: [UIView]) { clear() .hstack(withSpacing: spacing, padding: padding, diff --git a/Sources/SwiftUIKit/Containers/VStack.swift b/Sources/SwiftUIKit/Containers/VStack.swift index 81fe577..54bb19a 100644 --- a/Sources/SwiftUIKit/Containers/VStack.swift +++ b/Sources/SwiftUIKit/Containers/VStack.swift @@ -74,7 +74,7 @@ public class VStack: UIView { fatalError("init(coder:) has not been implemented") } - private func draw(views: [UIView]) { + internal func draw(views: [UIView]) { clear() .vstack(withSpacing: spacing, padding: padding, From 4501b97fae24ed6a07513e6112e44410d12c5a90 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:44:53 -0500 Subject: [PATCH 15/23] Fixed comment --- .../Core/BasicSwiftUIKitTests.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift b/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift index 08a9e75..dfa2698 100644 --- a/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift +++ b/Tests/SwiftUIKitTests/Core/BasicSwiftUIKitTests.swift @@ -296,7 +296,7 @@ final class BasicSwiftUIKitTests: XCTestCase { stack.views.value? += (0 ... 4).map { Label("\($0)") } stack.draw(views: stack.views.value ?? []) - + XCTAssert(stack.subviews.first.map { type(of: $0) } == UIStackView.self) XCTAssertEqual(stack.allSubviews.count, 7) XCTAssertEqual(stack.views.value?.count, 6) @@ -398,13 +398,16 @@ final class BasicSwiftUIKitTests: XCTestCase { } } - // Will fail unless... - //,= (iOS >= 13),= - XCTAssertEqual(switchView.allSubviews.count, 8) - XCTAssertEqual(uiSwitchView.allSubviews.count, 8) - XCTAssertEqual(view.allSubviews.count, 12) - XCTAssertEqual(otherView.allSubviews.count, 12) - //,= (End),= + /** Will fail unless... + === (iOS >= 13) === + */ + XCTAssertEqual(switchView.allSubviews.count, 8) + XCTAssertEqual(uiSwitchView.allSubviews.count, 8) + XCTAssertEqual(view.allSubviews.count, 12) + XCTAssertEqual(otherView.allSubviews.count, 12) + /** + === (End) === + */ XCTAssertEqual(viewWithoutSwitch.allSubviews.count, 3) From 9fa922e2b087cf33281d2370250d3ee17a76d7b6 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 19:45:56 -0500 Subject: [PATCH 16/23] Ignore .resolved --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 043dffd..cb39884 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /*.xcodeproj xcuserdata/ build +*.resolved \ No newline at end of file From a5b87280062c8e73799007aa8be6d81b74710623 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 20:04:05 -0500 Subject: [PATCH 17/23] Added quick contract function --- .../Extensions/UIAppearance+SwiftUIKit.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift diff --git a/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift b/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift new file mode 100644 index 0000000..2acd7dd --- /dev/null +++ b/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift @@ -0,0 +1,18 @@ +// +// File.swift +// +// +// Created by Zach Eriksen on 8/25/20. +// + +import UIKit +import Later + +@available(iOS 9.0, *) +public extension UIAppearance where Self: UIView { + func contract(_ closure: (Self) -> Contract) -> ContractView { + ContractView(view: self) { view in + closure(view) + } + } +} From 6ee2c36fb937e6a994906291d0df13a425a646bf Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Tue, 25 Aug 2020 20:06:11 -0500 Subject: [PATCH 18/23] Updated file header --- Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift b/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift index 2acd7dd..07b25ef 100644 --- a/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift +++ b/Sources/SwiftUIKit/Extensions/UIAppearance+SwiftUIKit.swift @@ -1,5 +1,5 @@ // -// File.swift +// UIAppearance+SwiftUIKit.swift // // // Created by Zach Eriksen on 8/25/20. From 1350453f8bf0d54308fc9e6342fad44e35e038a5 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Wed, 26 Aug 2020 08:09:24 -0500 Subject: [PATCH 19/23] Add UIView.later... --- .../SwiftUIKit/Extensions/UIView+Later.swift | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Sources/SwiftUIKit/Extensions/UIView+Later.swift diff --git a/Sources/SwiftUIKit/Extensions/UIView+Later.swift b/Sources/SwiftUIKit/Extensions/UIView+Later.swift new file mode 100644 index 0000000..d6e1c8d --- /dev/null +++ b/Sources/SwiftUIKit/Extensions/UIView+Later.swift @@ -0,0 +1,87 @@ +// +// UIView+Later.swift +// SwiftUIKit +// +// Created by CRi on 8/26/20. +// + +import UIKit +import Later + +@available(iOS 9.0, *) +public extension UIView { + static func later(_ laterView: (Later.Type) -> LaterValue) -> UIView { + let view = UIView() + + view.center { + LoadingView() + .start() + } + + laterView(Later.self) + .whenSuccess { embeddedView in + Later.main { + view.clear() + .embed { + embeddedView + } + } + + } + + return view + } + + static func later(centeredLoadingView: UIView, + withSize size: CGSize? = nil, + _ laterView: (Later.Type) -> LaterValue) -> UIView { + + let view = UIView() + + view.center { + centeredLoadingView + .configure { + if let size = size { + $0.frame(height: Float(size.height), + width: Float(size.width)) + } + } + } + + laterView(Later.self) + .whenSuccess { embeddedView in + Later.main { + view.clear() + .embed { + embeddedView + } + } + + } + + return view + } + + static func later(embeddedLoadingView: UIView, + withPadding padding: Float = 0, + _ laterView: (Later.Type) -> LaterValue) -> UIView { + let view = UIView() + + view.embed(withPadding: padding) { + embeddedLoadingView + } + + laterView(Later.self) + .whenSuccess { embeddedView in + Later.main { + view.clear() + .embed { + embeddedView + } + } + + } + + return view + } +} From 546b472c459b5706b5288440298088c38d684cf4 Mon Sep 17 00:00:00 2001 From: Zach Eriksen Date: Wed, 26 Aug 2020 08:12:08 -0500 Subject: [PATCH 20/23] CRi -> Name --- Sources/SwiftUIKit/Extensions/UIView+Later.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftUIKit/Extensions/UIView+Later.swift b/Sources/SwiftUIKit/Extensions/UIView+Later.swift index d6e1c8d..9b43ae6 100644 --- a/Sources/SwiftUIKit/Extensions/UIView+Later.swift +++ b/Sources/SwiftUIKit/Extensions/UIView+Later.swift @@ -2,7 +2,7 @@ // UIView+Later.swift // SwiftUIKit // -// Created by CRi on 8/26/20. +// Created by Zach Eriksen on 8/26/20. // import UIKit From f559b49f3870eab1ede57111cd844dd35c318a9c Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Wed, 26 Aug 2020 21:09:56 -0500 Subject: [PATCH 21/23] Allow optional actionm and add setter --- Sources/SwiftUIKit/Views/Button.swift | 14 ++++++++++---- Sources/SwiftUIKit/Views/NavButton.swift | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Sources/SwiftUIKit/Views/Button.swift b/Sources/SwiftUIKit/Views/Button.swift index dba3077..1b7f718 100644 --- a/Sources/SwiftUIKit/Views/Button.swift +++ b/Sources/SwiftUIKit/Views/Button.swift @@ -9,13 +9,13 @@ import UIKit @available(iOS 9.0, *) public class Button: UIButton { - private var action: () -> Void + private var action: (() -> Void)? public init(_ title: String, titleColor: UIColor? = nil, backgroundColor: UIColor? = nil, forEvent event: UIControl.Event = .touchUpInside, - _ action: @escaping () -> Void) { + _ action: (() -> Void)?) { self.action = action super.init(frame: .zero) @@ -27,7 +27,7 @@ public class Button: UIButton { accessibility(label: title, traits: .button) } - public init(_ action: @escaping () -> Void, + public init(action: (() -> Void)?, forEvent event: UIControl.Event = .touchUpInside, _ closure: () -> UIView) { self.action = action @@ -41,11 +41,17 @@ public class Button: UIButton { self.addTarget(self, action: #selector(handleButtonTap), for: event) } + public func setAction(_ action: (() -> Void)?) -> Self { + self.action = action + + return self + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func handleButtonTap() { - action() + action?() } } diff --git a/Sources/SwiftUIKit/Views/NavButton.swift b/Sources/SwiftUIKit/Views/NavButton.swift index 71590c4..67eca07 100644 --- a/Sources/SwiftUIKit/Views/NavButton.swift +++ b/Sources/SwiftUIKit/Views/NavButton.swift @@ -40,7 +40,7 @@ public class NavButton: Button { self.destination = destination self.style = style - super.init({ + super.init(action: { tapHandler?() Navigate.shared.go(destination(), style: style) From be247ab52fe70e6115d5b33a965ae36a93a4891a Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Wed, 26 Aug 2020 21:10:41 -0500 Subject: [PATCH 22/23] Added modifiers for text value --- .../Extensions/UILabel+SwiftUIKit.swift | 24 +++++++++++++++++++ .../Extensions/UITextField+SwiftUIKit.swift | 7 ++++++ .../Extensions/UITextView+SwiftUIKit.swift | 7 ++++++ 3 files changed, 38 insertions(+) create mode 100644 Sources/SwiftUIKit/Extensions/UILabel+SwiftUIKit.swift diff --git a/Sources/SwiftUIKit/Extensions/UILabel+SwiftUIKit.swift b/Sources/SwiftUIKit/Extensions/UILabel+SwiftUIKit.swift new file mode 100644 index 0000000..ac1c28a --- /dev/null +++ b/Sources/SwiftUIKit/Extensions/UILabel+SwiftUIKit.swift @@ -0,0 +1,24 @@ +// +// UILabel+SwiftUIKit.swift +// SwiftUIKit +// +// Created by Zach Eriksen on 7/7/20. +// + +import UIKit + +public extension UILabel { + @discardableResult + func text(color: UIColor?) -> Self { + textColor = color + + return self + } + + @discardableResult + func text(_ value: String?) -> Self { + text = value + + return self + } +} diff --git a/Sources/SwiftUIKit/Extensions/UITextField+SwiftUIKit.swift b/Sources/SwiftUIKit/Extensions/UITextField+SwiftUIKit.swift index 06a6043..d458de4 100644 --- a/Sources/SwiftUIKit/Extensions/UITextField+SwiftUIKit.swift +++ b/Sources/SwiftUIKit/Extensions/UITextField+SwiftUIKit.swift @@ -14,4 +14,11 @@ public extension UITextField { return self } + + @discardableResult + func text(_ value: String?) -> Self { + text = value + + return self + } } diff --git a/Sources/SwiftUIKit/Extensions/UITextView+SwiftUIKit.swift b/Sources/SwiftUIKit/Extensions/UITextView+SwiftUIKit.swift index 41f3b14..c788848 100644 --- a/Sources/SwiftUIKit/Extensions/UITextView+SwiftUIKit.swift +++ b/Sources/SwiftUIKit/Extensions/UITextView+SwiftUIKit.swift @@ -15,6 +15,13 @@ public extension UITextView { return self } + @discardableResult + func text(_ value: String?) -> Self { + text = value + + return self + } + @discardableResult func tint(color: UIColor?) -> Self { tintColor = color From f10e5fe41ae00ae581d7175f73f629705707b6c9 Mon Sep 17 00:00:00 2001 From: zmeriksen Date: Wed, 26 Aug 2020 21:35:27 -0500 Subject: [PATCH 23/23] Default action to nil and added discardableResult --- Sources/SwiftUIKit/Views/Button.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftUIKit/Views/Button.swift b/Sources/SwiftUIKit/Views/Button.swift index 1b7f718..d6ae60f 100644 --- a/Sources/SwiftUIKit/Views/Button.swift +++ b/Sources/SwiftUIKit/Views/Button.swift @@ -15,7 +15,7 @@ public class Button: UIButton { titleColor: UIColor? = nil, backgroundColor: UIColor? = nil, forEvent event: UIControl.Event = .touchUpInside, - _ action: (() -> Void)?) { + _ action: (() -> Void)? = nil) { self.action = action super.init(frame: .zero) @@ -27,7 +27,7 @@ public class Button: UIButton { accessibility(label: title, traits: .button) } - public init(action: (() -> Void)?, + public init(action: (() -> Void)? = nil, forEvent event: UIControl.Event = .touchUpInside, _ closure: () -> UIView) { self.action = action @@ -41,6 +41,7 @@ public class Button: UIButton { self.addTarget(self, action: #selector(handleButtonTap), for: event) } + @discardableResult public func setAction(_ action: (() -> Void)?) -> Self { self.action = action