Skip to content

Commit 5714575

Browse files
committed
Merge pull request #48 from neilpa/predicates
Property predicates
2 parents fc53a95 + d49a6f9 commit 5714575

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

Rex.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; };
4444
D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD91B34B3F0001A89B3 /* Action.swift */; };
4545
D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBDC1B34B691001A89B3 /* UIButton.swift */; };
46+
D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; };
47+
D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; };
48+
D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; };
49+
D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; };
4650
D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; };
4751
D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; };
4852
D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; };
@@ -129,6 +133,8 @@
129133
D86FFBD71B34B242001A89B3 /* UILabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = "<group>"; };
130134
D86FFBD91B34B3F0001A89B3 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
131135
D86FFBDC1B34B691001A89B3 /* UIButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
136+
D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = "<group>"; };
137+
D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = "<group>"; };
132138
D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelTests.swift; sourceTree = "<group>"; };
133139
D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSData.swift; sourceTree = "<group>"; };
134140
D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaults.swift; sourceTree = "<group>"; };
@@ -197,6 +203,7 @@
197203
isa = PBXGroup;
198204
children = (
199205
D86FFBD91B34B3F0001A89B3 /* Action.swift */,
206+
D8A454051BD26A1A00C9E790 /* Property.swift */,
200207
D8003EBC1AFED01000D7D3C5 /* Signal.swift */,
201208
D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */,
202209
4238D5941B4D593E008534C0 /* AppKit */,
@@ -219,6 +226,7 @@
219226
D8003E9D1AFEC3D400D7D3C5 /* Tests */ = {
220227
isa = PBXGroup;
221228
children = (
229+
D8A454081BD2772700C9E790 /* PropertyTests.swift */,
222230
D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */,
223231
D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */,
224232
D8F097461B17F5BF002E15BA /* Foundation */,
@@ -489,6 +497,7 @@
489497
isa = PBXSourcesBuildPhase;
490498
buildActionMask = 2147483647;
491499
files = (
500+
D8A454061BD26A1A00C9E790 /* Property.swift in Sources */,
492501
D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */,
493502
D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */,
494503
D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */,
@@ -507,6 +516,7 @@
507516
D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */,
508517
D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */,
509518
D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */,
519+
D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */,
510520
);
511521
runOnlyForDeploymentPostprocessing = 0;
512522
};
@@ -521,6 +531,7 @@
521531
D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */,
522532
D834572D1AFEE45B0070616A /* Signal.swift in Sources */,
523533
D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */,
534+
D8A454071BD26A1A00C9E790 /* Property.swift in Sources */,
524535
D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */,
525536
D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */,
526537
D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */,
@@ -540,6 +551,7 @@
540551
8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */,
541552
D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */,
542553
8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */,
554+
D8A4540A1BD2772700C9E790 /* PropertyTests.swift in Sources */,
543555
D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */,
544556
D83457411AFEE6050070616A /* SignalTests.swift in Sources */,
545557
8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */,

Source/Property.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//
2+
// Property.swift
3+
// Rex
4+
//
5+
// Created by Neil Pankey on 10/17/15.
6+
// Copyright (c) 2015 Neil Pankey. All rights reserved.
7+
//
8+
9+
import ReactiveCocoa
10+
11+
extension PropertyType where Value == Bool {
12+
public func and<P: PropertyType where P.Value == Bool>(other: P) -> AndProperty {
13+
return AndProperty(terms: [AnyProperty(self), AnyProperty(other)])
14+
}
15+
16+
public func and(other: AnyProperty<Bool>) -> AndProperty {
17+
return AndProperty(terms: [AnyProperty(self), other])
18+
}
19+
20+
public func or<P: PropertyType where P.Value == Bool>(other: P) -> OrProperty {
21+
return OrProperty(terms: [AnyProperty(self), AnyProperty(other)])
22+
}
23+
24+
public func or(other: AnyProperty<Bool>) -> OrProperty {
25+
return OrProperty(terms: [AnyProperty(self), other])
26+
}
27+
28+
public func not() -> NotProperty {
29+
return NotProperty(source: AnyProperty(self), invert: true)
30+
}
31+
}
32+
33+
public struct AndProperty: PropertyType {
34+
public let terms: [AnyProperty<Bool>]
35+
36+
public var value: Bool {
37+
return terms.reduce(true) { $0 && $1.value }
38+
}
39+
40+
public var producer: SignalProducer<Bool, NoError> {
41+
let producers = terms.map { $0.producer }
42+
return combineLatest(producers).map { values in
43+
return values.reduce(true) { $0 && $1 }
44+
}
45+
}
46+
47+
public func and<P : PropertyType where P.Value == Bool>(other: P) -> AndProperty {
48+
return AndProperty(terms: terms + [AnyProperty(other)])
49+
}
50+
51+
public func and(other: AnyProperty<Bool>) -> AndProperty {
52+
return AndProperty(terms: terms + [other])
53+
}
54+
55+
private init(terms: [AnyProperty<Bool>]) {
56+
self.terms = terms
57+
}
58+
}
59+
60+
public struct OrProperty: PropertyType {
61+
public let terms: [AnyProperty<Bool>]
62+
63+
public var value: Bool {
64+
return terms.reduce(false) { $0 || $1.value }
65+
}
66+
67+
public var producer: SignalProducer<Bool, NoError> {
68+
let producers = terms.map { $0.producer }
69+
return combineLatest(producers).map { values in
70+
return values.reduce(false) { $0 || $1 }
71+
}
72+
}
73+
74+
public func or<P : PropertyType where P.Value == Bool>(other: P) -> OrProperty {
75+
return OrProperty(terms: terms + [AnyProperty(other)])
76+
}
77+
78+
public func or(other: AnyProperty<Bool>) -> OrProperty {
79+
return OrProperty(terms: terms + [other])
80+
}
81+
82+
private init(terms: [AnyProperty<Bool>]) {
83+
self.terms = terms
84+
}
85+
}
86+
87+
public struct NotProperty: PropertyType {
88+
private let source: AnyProperty<Bool>
89+
private let invert: Bool
90+
91+
public var value: Bool {
92+
return source.value != invert
93+
}
94+
95+
public var producer: SignalProducer<Bool, NoError> {
96+
return source.producer.map { $0 != self.invert }
97+
}
98+
99+
public func not() -> NotProperty {
100+
return NotProperty(source: source, invert: !invert)
101+
}
102+
103+
private init(source: AnyProperty<Bool>, invert: Bool) {
104+
self.source = source
105+
self.invert = invert
106+
}
107+
}

Tests/PropertyTests.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// PropertyTests.swift
3+
// Rex
4+
//
5+
// Created by Neil Pankey on 10/17/15.
6+
// Copyright (c) 2015 Neil Pankey. All rights reserved.
7+
//
8+
9+
@testable import Rex
10+
import ReactiveCocoa
11+
import XCTest
12+
13+
final class PropertyTests: XCTestCase {
14+
15+
func testAndProperty() {
16+
let lhs = MutableProperty(false), rhs = MutableProperty(false)
17+
let and = lhs.and(rhs)
18+
19+
var current: Bool!
20+
and.producer.startWithNext { current = $0 }
21+
22+
XCTAssertFalse(and.value)
23+
XCTAssertFalse(current!)
24+
25+
lhs.value = true
26+
XCTAssertFalse(and.value)
27+
XCTAssertFalse(current!)
28+
29+
rhs.value = true
30+
XCTAssertTrue(and.value)
31+
XCTAssertTrue(current!)
32+
33+
let (signal, pipe) = Signal<Bool, NoError>.pipe()
34+
let and2 = and.and(AnyProperty(initialValue: false, signal: signal))
35+
and2.producer.startWithNext { current = $0 }
36+
37+
XCTAssertFalse(and2.value)
38+
XCTAssertFalse(current!)
39+
40+
pipe.sendNext(true)
41+
XCTAssertTrue(and2.value)
42+
XCTAssertTrue(current!)
43+
}
44+
45+
func testOrProperty() {
46+
let lhs = MutableProperty(true), rhs = MutableProperty(true)
47+
let or = lhs.or(rhs)
48+
49+
var current: Bool!
50+
or.producer.startWithNext { current = $0 }
51+
52+
XCTAssertTrue(or.value)
53+
XCTAssertTrue(current!)
54+
55+
lhs.value = false
56+
XCTAssertTrue(or.value)
57+
XCTAssertTrue(current!)
58+
59+
rhs.value = false
60+
XCTAssertFalse(or.value)
61+
XCTAssertFalse(current!)
62+
63+
let (signal, pipe) = Signal<Bool, NoError>.pipe()
64+
let or2 = or.or(AnyProperty(initialValue: true, signal: signal))
65+
or2.producer.startWithNext { current = $0 }
66+
67+
XCTAssertTrue(or2.value)
68+
XCTAssertTrue(current!)
69+
70+
pipe.sendNext(false)
71+
XCTAssertFalse(or2.value)
72+
XCTAssertFalse(current!)
73+
}
74+
75+
func testNotProperty() {
76+
let source = MutableProperty(false)
77+
let not = source.not()
78+
79+
var current: Bool!
80+
not.producer.startWithNext { current = $0 }
81+
82+
XCTAssertTrue(not.value)
83+
XCTAssertTrue(current!)
84+
85+
source.value = true
86+
XCTAssertFalse(not.value)
87+
XCTAssertFalse(current!)
88+
}
89+
}

0 commit comments

Comments
 (0)