Skip to content

Commit

Permalink
Merge pull request #3 from timsearle/reduce-and-tests
Browse files Browse the repository at this point in the history
Adding `reduce`
  • Loading branch information
timsearle authored Oct 8, 2019
2 parents 80cceda + 303109c commit 81545d4
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 32 deletions.
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Bind.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
8755A7462347A93800C8AD07 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A73E2347A88D00C8AD07 /* RelayTests.swift */; };
8755A7472347A93800C8AD07 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A73E2347A88D00C8AD07 /* RelayTests.swift */; };
8755A7482347A93E00C8AD07 /* BindableTestsUIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A73C23479B5700C8AD07 /* BindableTestsUIKit.swift */; };
8755A74A234B508900C8AD07 /* UnbindableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A749234B508900C8AD07 /* UnbindableMock.swift */; };
8755A74B234B509000C8AD07 /* UnbindableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A749234B508900C8AD07 /* UnbindableMock.swift */; };
8755A74C234B509000C8AD07 /* UnbindableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A749234B508900C8AD07 /* UnbindableMock.swift */; };
8755A74D234B509000C8AD07 /* UnbindableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8755A749234B508900C8AD07 /* UnbindableMock.swift */; };
8774D75FE1168D2F1D4CE056 /* Bindable+UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9B94AF56A71DFBCA50413B /* Bindable+UIImageView.swift */; };
8916D29F0AEF2B864EED6B53 /* Bindable+UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8B8A3AE507973B94E0E2A1 /* Bindable+UILabel.swift */; };
8A9566D706D206E76327E3DB /* Bindable+NSLayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC8C0368846079A6AB4E3A8 /* Bindable+NSLayoutConstraint.swift */; };
Expand Down Expand Up @@ -233,6 +237,7 @@
8755A73C23479B5700C8AD07 /* BindableTestsUIKit.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = BindableTestsUIKit.swift; sourceTree = "<group>"; tabWidth = 4; };
8755A73E2347A88D00C8AD07 /* RelayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayTests.swift; sourceTree = "<group>"; };
8755A7402347A92500C8AD07 /* SubscriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionTests.swift; sourceTree = "<group>"; };
8755A749234B508900C8AD07 /* UnbindableMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnbindableMock.swift; sourceTree = "<group>"; };
87BCBA1C96D1499A2368C6C4 /* Bindable+UIViewController.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = "Bindable+UIViewController.swift"; sourceTree = "<group>"; tabWidth = 4; };
8C9B94AF56A71DFBCA50413B /* Bindable+UIImageView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = "Bindable+UIImageView.swift"; sourceTree = "<group>"; tabWidth = 4; };
91D93EE6FE99882FF6B5A4E3 /* BindTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BindTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -372,6 +377,7 @@
children = (
643F9E946B7E3D2F8E2975BD /* PrinterMock.swift */,
DBCF98045AC9F17CD5EF1563 /* BindableMock.swift */,
8755A749234B508900C8AD07 /* UnbindableMock.swift */,
);
path = Mock;
sourceTree = "<group>";
Expand Down Expand Up @@ -667,6 +673,7 @@
DEEB7E76CA6E133DCE59BD82 /* PrinterMock.swift in Sources */,
8755A73F2347A88D00C8AD07 /* RelayTests.swift in Sources */,
8755A7412347A92500C8AD07 /* SubscriptionTests.swift in Sources */,
8755A74A234B508900C8AD07 /* UnbindableMock.swift in Sources */,
2D1C7E116D41AC6269445022 /* BindableMock.swift in Sources */,
637549C2D38B6A9E34E3C05E /* Output+Extension.swift in Sources */,
8755A73D23479B5700C8AD07 /* BindableTestsUIKit.swift in Sources */,
Expand Down Expand Up @@ -741,6 +748,7 @@
16B22C259512A6A4DB492648 /* BindableMock.swift in Sources */,
F37FB2DC1E254424AC2BAE03 /* Output+Extension.swift in Sources */,
1DB68B189147E165DF6BB137 /* OutputTests.swift in Sources */,
8755A74D234B509000C8AD07 /* UnbindableMock.swift in Sources */,
8755A7442347A93500C8AD07 /* SubscriptionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -810,6 +818,7 @@
A8428486B13DF705F151FDD6 /* PrinterMock.swift in Sources */,
8755A7452347A93700C8AD07 /* RelayTests.swift in Sources */,
28AB432723BFCC656F66C488 /* BindableMock.swift in Sources */,
8755A74B234B509000C8AD07 /* UnbindableMock.swift in Sources */,
B603ED1DF83FB4467FB9B868 /* Output+Extension.swift in Sources */,
6F853E9E0052D86B01B6F4F3 /* OutputTests.swift in Sources */,
8755A7482347A93E00C8AD07 /* BindableTestsUIKit.swift in Sources */,
Expand All @@ -826,6 +835,7 @@
9D059CF3C7783B55F63C17CB /* BindableMock.swift in Sources */,
23ED5475B3FF87CBAF7B1701 /* Output+Extension.swift in Sources */,
42DCB57764A902476445A1ED /* OutputTests.swift in Sources */,
8755A74C234B509000C8AD07 /* UnbindableMock.swift in Sources */,
8755A7432347A93400C8AD07 /* SubscriptionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
15 changes: 10 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import PackageDescription

let package = Package(
name: "Trigger",
name: "Bind",
platforms: [
.iOS(.v11),
.macOS(.v10_12)
],
products: [
.library(
name: "Trigger", targets: ["Trigger"])
name: "Bind", targets: ["Bind"])
],
targets: [
.target( name: "Trigger"),
.testTarget( name: "TriggerTests", dependencies: ["Trigger"])
]
.target( name: "Bind"),
.testTarget( name: "BindTests", dependencies: ["Bind"])
],
swiftLanguageVersions: [.v5]
)
1 change: 0 additions & 1 deletion Sources/Bind/Bindables/UIKit/Bindable+UIControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import UIKit

public extension Bindable where TargetType: UIControl {

var isSelected: Binder<Bool> {
return Binder<Bool>(self.target) { control, isSelected in
control.isSelected = isSelected
Expand Down
31 changes: 24 additions & 7 deletions Sources/Bind/Output.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ public extension Output {
return output
}

/**
`filter` passes the Value through a predicate, if the functions true the `Value` is output, otherwise
it is filtered out.
- Parameter filter: The function that predicates on the `Value` to determine if it is output
- Returns: A new `Output` which predicates on the `Value` before outputting
*/
func filter(_ filter: @escaping (Value) -> Bool) -> Output<Value> {
let output = Output<Value>()

Expand All @@ -198,14 +204,25 @@ public extension Output {

return output
}
}

// MARK: - Typed extensions
public extension Output where Value == Bool {
func invert() {
guard let currentValue = value else {
return
/**
Returns a new output that is the result of combining the outputted elements of the receiver
using the given closure.
- Parameter initial: The value to use as the initial accumulating value.
initialResult is passed to nextPartialResult the first time the closure is executed.
- Parameter nextPartialResult:
A closure that combines an accumulating value and the next value of the Output into a new accumulating
value that is then output to any binders.
- Returns: A new `Output` which acts as a tap of the combined accumulating values
*/
func reduce<Result>(initial: Result, nextPartialResult: @escaping (Result, Value) -> Result) -> Output<Result> {
let output = Output<Result>()

bind { value in
let result = nextPartialResult(output.value ?? initial, value)
output.update(withValue: result)
}
update(withValue: !currentValue)

return output
}
}
4 changes: 3 additions & 1 deletion Sources/Bind/Subscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension Subscription: Hashable {
}

public final class SubscriptionContainer {
private var container: [Subscription] = []
var container: [Subscription] = []

public init() {}

Expand All @@ -41,5 +41,7 @@ public final class SubscriptionContainer {
for subscription in container {
subscription.unsubscribe()
}

container = []
}
}
93 changes: 93 additions & 0 deletions Tests/BindTests/BindableTestsUIKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import XCTest
final class BindableTestsUIKit: XCTestCase {
func testLabel() {
let label = UILabel()
label.textColor = .black
let attributedLabel = UILabel()

let textOutput = Output<String>()
Expand All @@ -29,5 +30,97 @@ final class BindableTestsUIKit: XCTestCase {
attributedTextOutput.update(withValue: NSAttributedString(string: "text"))
XCTAssertEqual(attributedLabel.attributedText, NSAttributedString(string: "text"))
}

func testViewBools() {
let view = UIView()

let hiddenOutput = Output<Bool>()
let visibleOutput = Output<Bool>()
let visibleAlpha = Output<Bool>()
let visibleAlphaAnimated = Output<Bool>()
let userInteractionEnabledOutput = Output<Bool>()
let constraintsActive = Output<Bool>()

hiddenOutput.bind(to: view.binding.isHidden)
visibleOutput.bind(to: view.binding.isVisible)
visibleAlpha.bind(to: view.binding.isVisibleAlpha(animated: false))
visibleAlphaAnimated.bind(to: view.binding.isVisibleAlpha(animated: true))
userInteractionEnabledOutput.bind(to: view.binding.isUserInteractionEnabled)
constraintsActive.bind(to: view.binding.areConstraintsActive)

hiddenOutput.update(withValue: true)
XCTAssertEqual(view.isHidden, true)

visibleOutput.update(withValue: true)
XCTAssertEqual(view.isHidden, false)

visibleAlpha.update(withValue: false)
XCTAssertEqual(view.alpha, 0)

visibleAlpha.update(withValue: true)
XCTAssertEqual(view.alpha, 1)

visibleAlphaAnimated.update(withValue: false)
XCTAssertEqual(view.alpha, 0)

visibleAlphaAnimated.update(withValue: true)
XCTAssertEqual(view.alpha, 1)

userInteractionEnabledOutput.update(withValue: false)
XCTAssertEqual(view.isUserInteractionEnabled, false)

view.heightAnchor.constraint(equalToConstant: 10).isActive = true
XCTAssertEqual(view.constraints.count, 1)
constraintsActive.update(withValue: false)
XCTAssertEqual(view.constraints.count, 0)
}

func testViewUIColors() {
let view = UIView()

let backgroundColorOutput = Output<UIColor>()
let borderColorOutput = Output<UIColor>()
let tintColorOutput = Output<UIColor>()

backgroundColorOutput.bind(to: view.binding.backgroundColor)
borderColorOutput.bind(to: view.binding.borderColor)
tintColorOutput.bind(to: view.binding.tintColor)

backgroundColorOutput.update(withValue: .red)
XCTAssertEqual(view.backgroundColor, .red)

borderColorOutput.update(withValue: .green)
XCTAssertEqual(view.layer.borderColor, UIColor.green.cgColor)

tintColorOutput.update(withValue: .blue)
XCTAssertEqual(view.tintColor, .blue)
}

func testViewCGFloat() {
let view = UIView()

let borderWidthOutput = Output<CGFloat>()
let cornerRadiusOutput = Output<CGFloat>()

borderWidthOutput.bind(to: view.binding.borderWidth)
cornerRadiusOutput.bind(to: view.binding.cornerRadius)

borderWidthOutput.update(withValue: 30)
XCTAssertEqual(view.layer.borderWidth, 30)

cornerRadiusOutput.update(withValue: 40)
XCTAssertEqual(view.layer.cornerRadius, 40)
}

func testViewString() {
let view = UIView()

let accessibilityOutput = Output<String>()

accessibilityOutput.bind(to: view.binding.accessibilityIdentifier)

accessibilityOutput.update(withValue: "test-identifier")
XCTAssertEqual(view.accessibilityIdentifier, "test-identifier")
}
}
#endif
10 changes: 10 additions & 0 deletions Tests/BindTests/Mock/UnbindableMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@testable import Bind

final class UnbindableMock: Unbindable {
var unbindCalledCount = 0
var unbindSubscriptionArray = [Subscription]()
func unbind(for subscription: Subscription) {
unbindCalledCount += 1
unbindSubscriptionArray.append(subscription)
}
}
53 changes: 35 additions & 18 deletions Tests/BindTests/OutputTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,6 @@ final class OutputTests: XCTestCase {
XCTAssertEqual(testObjectTwo.text, "Test")
}

func testToggleWithValue() {
let output = Output(value: true)
XCTAssertEqual(output.latest, true)

output.invert()

XCTAssertEqual(output.latest, false)
}

func testToggleWithNoValue() {
let output = Output<Bool>()
XCTAssertNil(output.latest)

output.invert()
XCTAssertNil(output.latest)
}

func testUnbind() {
let testObject = BindableMock()

Expand Down Expand Up @@ -221,7 +204,7 @@ final class OutputTests: XCTestCase {

value.update(withValue: .one)
XCTAssertEqual(mappedValue.latest, "one")
}
}

func testFlatMap() {
//swiftlint:disable:next nesting
Expand Down Expand Up @@ -292,6 +275,40 @@ final class OutputTests: XCTestCase {
XCTAssertEqual(merge.latest, 5)
}

func testReduceReferenceType() {
class TestObject { //swiftlint:disable:this nesting
var currentString: String = ""
}

let initial = Output<Int>()

let reduced = initial
.reduce(initial: TestObject()) { current, number -> TestObject in
var currentString = current.currentString
currentString += "\(number)"
current.currentString = currentString
return current
}

for value in [1, 2, 3, 4, 5] {
initial.update(withValue: value)
}

XCTAssertEqual(reduced.latest?.currentString, "12345")
}

func testReduceValueType() {
let initial = Output<Int>()

let reduced = initial.reduce(initial: 0, nextPartialResult: +)

for value in [1, 2, 3, 4, 5] {
initial.update(withValue: value)
}

XCTAssertEqual(reduced.latest, 15)
}

func testDebug() {
let printer = PrinterMock()
let output1 = Output<Bool>(printer: printer)
Expand Down
34 changes: 34 additions & 0 deletions Tests/BindTests/SubscriptionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,39 @@ import XCTest
@testable import Bind

final class SubscriptionTests: XCTestCase {
func testUnsubscribe() {
let mockUnbindable = UnbindableMock()
let subscription = Subscription(unbinder: mockUnbindable)

subscription.unsubscribe()

XCTAssertEqual(mockUnbindable.unbindCalledCount, 1)
XCTAssertEqual(mockUnbindable.unbindSubscriptionArray.count, 1)
XCTAssertEqual(mockUnbindable.unbindSubscriptionArray.first, subscription)
}

func testSubscriptionContainerAdd() {
let mockUnbindable = UnbindableMock()
let subscription = Subscription(unbinder: mockUnbindable)
let container = SubscriptionContainer()

subscription.add(to: container)

XCTAssertEqual(container.container.count, 1)
}

func testSubscriptionContainerUnsubscribe() {
let mockUnbindable = UnbindableMock()
let subscription = Subscription(unbinder: mockUnbindable)

let container = SubscriptionContainer()
subscription.add(to: container)

XCTAssertEqual(container.container.count, 1)

container.unsubscribe()

XCTAssertEqual(container.container.count, 0)
XCTAssertEqual(mockUnbindable.unbindCalledCount, 1)
}
}

0 comments on commit 81545d4

Please sign in to comment.