Skip to content

Commit 8e215f3

Browse files
authored
Merge branch 'master' into nesting-validating-prop
2 parents e9b8e58 + dadbb19 commit 8e215f3

File tree

4 files changed

+121
-39
lines changed

4 files changed

+121
-39
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
1. `ValidationResult` and `ValidatorOutput` have been renamed to `ValidatingProperty.Result` and `ValidatingProperty.Decision`, respectively. (#443)
55

6+
1. In Swift 3.2 or later, you may create `BindingTarget` for a key path of a specific object. (#440, kudos to @andersio)
7+
68
# 2.0.0-alpha.2
79
1. In Swift 3.2 or later, you can use `map()` with the new Smart Key Paths. (#435, kudos to @sharplet)
810

Sources/UnidirectionalBinding.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,30 @@ public struct BindingTarget<Value>: BindingTargetProvider {
148148
}
149149
self.init(lifetime: lifetime, action: setter)
150150
}
151+
152+
#if swift(>=3.2)
153+
// `Lifetime` is required on these overloads. RAC would provide convenience overloads
154+
// for these with `lifetime(of:)`.
155+
156+
/// Creates a binding target.
157+
///
158+
/// - parameters:
159+
/// - lifetime: The expected lifetime of any bindings towards `self`.
160+
/// - object: The object to consume values.
161+
/// - keyPath: The key path of the object that consumes values.
162+
public init<Object: AnyObject>(lifetime: Lifetime, object: Object, keyPath: ReferenceWritableKeyPath<Object, Value>) {
163+
self.init(lifetime: lifetime) { [weak object] in object?[keyPath: keyPath] = $0 }
164+
}
165+
166+
/// Creates a binding target which consumes values on the specified scheduler.
167+
///
168+
/// - parameters:
169+
/// - scheduler: The scheduler on which the `setter` consumes the values.
170+
/// - lifetime: The expected lifetime of any bindings towards `self`.
171+
/// - object: The object to consume values.
172+
/// - keyPath: The key path of the object that consumes values.
173+
public init<Object: AnyObject>(on scheduler: Scheduler, lifetime: Lifetime, object: Object, keyPath: ReferenceWritableKeyPath<Object, Value>) {
174+
self.init(on: scheduler, lifetime: lifetime) { [weak object] in object?[keyPath: keyPath] = $0 }
175+
}
176+
#endif
151177
}

Tests/ReactiveSwiftTests/UnidirectionalBindingSpec.swift

Lines changed: 88 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,135 @@ import Nimble
55
import Quick
66
@testable import ReactiveSwift
77

8+
private class Object {
9+
var value: Int = 0
10+
}
11+
812
class UnidirectionalBindingSpec: QuickSpec {
913
override func spec() {
1014
describe("BindingTarget") {
1115
var token: Lifetime.Token!
1216
var lifetime: Lifetime!
13-
var target: BindingTarget<Int>!
14-
var optionalTarget: BindingTarget<Int?>!
15-
var value: Int?
1617

1718
beforeEach {
1819
token = Lifetime.Token()
1920
lifetime = Lifetime(token)
20-
target = BindingTarget(lifetime: lifetime, action: { value = $0 })
21-
optionalTarget = BindingTarget(lifetime: lifetime, action: { value = $0 })
22-
value = nil
2321
}
2422

25-
describe("non-optional target") {
26-
it("should pass through the lifetime") {
27-
expect(target.lifetime).to(beIdenticalTo(lifetime))
23+
describe("closure binding target") {
24+
var target: BindingTarget<Int>!
25+
var optionalTarget: BindingTarget<Int?>!
26+
var value: Int?
27+
28+
beforeEach {
29+
target = BindingTarget(lifetime: lifetime, action: { value = $0 })
30+
optionalTarget = BindingTarget(lifetime: lifetime, action: { value = $0 })
31+
value = nil
2832
}
2933

30-
it("should trigger the supplied setter") {
31-
expect(value).to(beNil())
34+
describe("non-optional target") {
35+
it("should pass through the lifetime") {
36+
expect(target.lifetime).to(beIdenticalTo(lifetime))
37+
}
3238

33-
target.action(1)
34-
expect(value) == 1
39+
it("should trigger the supplied setter") {
40+
expect(value).to(beNil())
41+
42+
target.action(1)
43+
expect(value) == 1
44+
}
45+
46+
it("should accept bindings from properties") {
47+
expect(value).to(beNil())
48+
49+
let property = MutableProperty(1)
50+
target <~ property
51+
expect(value) == 1
52+
53+
property.value = 2
54+
expect(value) == 2
55+
}
3556
}
3657

37-
it("should accept bindings from properties") {
38-
expect(value).to(beNil())
58+
describe("optional target") {
59+
it("should pass through the lifetime") {
60+
expect(optionalTarget.lifetime).to(beIdenticalTo(lifetime))
61+
}
3962

40-
let property = MutableProperty(1)
41-
target <~ property
42-
expect(value) == 1
63+
it("should trigger the supplied setter") {
64+
expect(value).to(beNil())
4365

44-
property.value = 2
45-
expect(value) == 2
66+
optionalTarget.action(1)
67+
expect(value) == 1
68+
}
69+
70+
it("should accept bindings from properties") {
71+
expect(value).to(beNil())
72+
73+
let property = MutableProperty(1)
74+
optionalTarget <~ property
75+
expect(value) == 1
76+
77+
property.value = 2
78+
expect(value) == 2
79+
}
4680
}
4781
}
4882

49-
describe("optional target") {
83+
#if swift(>=3.2)
84+
describe("key path binding target") {
85+
var target: BindingTarget<Int>!
86+
var object: Object!
87+
88+
beforeEach {
89+
object = Object()
90+
target = BindingTarget(lifetime: lifetime, object: object, keyPath: \.value)
91+
}
92+
5093
it("should pass through the lifetime") {
51-
expect(optionalTarget.lifetime).to(beIdenticalTo(lifetime))
94+
expect(target.lifetime).to(beIdenticalTo(lifetime))
5295
}
5396

5497
it("should trigger the supplied setter") {
55-
expect(value).to(beNil())
98+
expect(object.value) == 0
5699

57-
optionalTarget.action(1)
58-
expect(value) == 1
100+
target.action(1)
101+
expect(object.value) == 1
59102
}
60103

61104
it("should accept bindings from properties") {
62-
expect(value).to(beNil())
105+
expect(object.value) == 0
63106

64107
let property = MutableProperty(1)
65-
optionalTarget <~ property
66-
expect(value) == 1
108+
target <~ property
109+
expect(object.value) == 1
67110

68111
property.value = 2
69-
expect(value) == 2
112+
expect(object.value) == 2
70113
}
71114
}
115+
#endif
72116

73117
it("should not deadlock on the same queue") {
74-
target = BindingTarget(on: UIScheduler(),
75-
lifetime: lifetime,
76-
action: { value = $0 })
118+
var value: Int?
119+
120+
let target = BindingTarget(on: UIScheduler(),
121+
lifetime: lifetime,
122+
action: { value = $0 })
77123

78124
let property = MutableProperty(1)
79125
target <~ property
80126
expect(value) == 1
81127
}
82128

83129
it("should not deadlock on the main thread even if the context was switched to a different queue") {
130+
var value: Int?
131+
84132
let queue = DispatchQueue(label: #file)
85133

86-
target = BindingTarget(on: UIScheduler(),
87-
lifetime: lifetime,
88-
action: { value = $0 })
134+
let target = BindingTarget(on: UIScheduler(),
135+
lifetime: lifetime,
136+
action: { value = $0 })
89137

90138
let property = MutableProperty(1)
91139

@@ -97,6 +145,8 @@ class UnidirectionalBindingSpec: QuickSpec {
97145
}
98146

99147
it("should not deadlock even if the value is originated from the same queue indirectly") {
148+
var value: Int?
149+
100150
let key = DispatchSpecificKey<Void>()
101151
DispatchQueue.main.setSpecific(key: key, value: ())
102152

@@ -107,9 +157,9 @@ class UnidirectionalBindingSpec: QuickSpec {
107157
mainQueueCounter.modify { $0 += DispatchQueue.getSpecific(key: key) != nil ? 1 : 0 }
108158
}
109159

110-
target = BindingTarget(on: UIScheduler(),
111-
lifetime: lifetime,
112-
action: setter)
160+
let target = BindingTarget(on: UIScheduler(),
161+
lifetime: lifetime,
162+
action: setter)
113163

114164
let scheduler: QueueScheduler
115165
if #available(OSX 10.10, *) {

script/gen-docs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ cp ../Logo/PNG/logo-Swift.png ${DOC_PATH}/Logo/PNG/logo-Swift.png
6666
cp ../Logo/PNG/JoinSlack.png ${DOC_PATH}/Logo/PNG/JoinSlack.png
6767
cp ../Logo/PNG/Docs.png ${DOC_PATH}/Logo/PNG/Docs.png
6868

69+
# Fix all readme links.
70+
perl -0777 -i -pe 's/"Documentation\/([a-zA-Z0-9-_]+)\.md/"\L$1\.html/g' ${DOC_PATH}/*.html
71+
perl -0777 -i -pe 's/"(a-zA-Z0-9-_]+)\.md/"\L$1\.html/g' ${DOC_PATH}/*.html
72+
6973
git add ${DOC_PATH}
7074

7175
# Ensure Jekyll is not running in `docs`.
@@ -78,7 +82,7 @@ git add ./reactiveswift/docs/latest
7882

7983
# Update the docset feed.
8084
rm ./reactiveswift/docs/ReactiveSwift.xml
81-
cp ../script/feed.xml.template ./reactiveswift/docs/ReactiveSwift.xml
85+
/bin/cp -f ../script/feed.xml.template ./reactiveswift/docs/ReactiveSwift.xml
8286
sed -i -- "s/FRAMEWORK_VERSION/${TRAVIS_TAG}/g" ./reactiveswift/docs/ReactiveSwift.xml
8387
git add ./reactiveswift/docs/ReactiveSwift.xml
8488

0 commit comments

Comments
 (0)