Skip to content

Commit 3d9ccab

Browse files
authored
Mark Property and MutableProperty as property wrappers. (#781)
* Mark Property and MutableProperty as property wrappers. * Update changelog.
1 parent f347ff8 commit 3d9ccab

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

CHANGELOG.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
# master
22
*Please add new entries at the top.*
33

4-
1. Joining an empty sequence of producers can now send an event on the joined signal producer by providing the `noUpstreamSentinel` parameter. This becomes relevant, when the sequence of producers is calculated from some other Signal and the signal resulting from the joined producers is observed. If no event is sent only when the producers sequence is empty, then the observer gets stalled and e.g. the ui won't update.
4+
1. `Property` and `MutableProperty` can now be used as property wrapper. Note that they remain a reference type container, so it may not be appropriate to use them in types requiring value semantics. (#781)
5+
```swift
6+
class ViewModel {
7+
@MutableProperty var count: Int = 0
8+
9+
func subscribe() {
10+
self.$count.producer.startWithValues {
11+
print("`count` has changed to \(count)")
12+
}
13+
}
14+
15+
func increment() {
16+
print("count prior to increment: \(count)")
17+
self.$count.modify { $0 += 1 }
18+
}
19+
}
20+
```
21+
22+
1. Joining an empty sequence of producers can now send an event on the joined signal producer by providing the `noUpstreamSentinel` parameter. This becomes relevant, when the sequence of producers is calculated from some other Signal and the signal resulting from the joined producers is observed. If no event is sent only when the producers sequence is empty, then the observer gets stalled and e.g. the ui won't update. (#774, kudos to @rocketnik)
523

624
# 6.2.1
725

Sources/Property.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ extension PropertyProtocol where Value == Bool {
470470
/// deinitializing.
471471
///
472472
/// Note that composed properties do not retain any of its sources.
473+
@propertyWrapper
473474
public final class Property<Value>: PropertyProtocol {
474475
private let _value: () -> Value
475476

@@ -478,6 +479,16 @@ public final class Property<Value>: PropertyProtocol {
478479
return _value()
479480
}
480481

482+
@inlinable
483+
public var wrappedValue: Value {
484+
return value
485+
}
486+
487+
@inlinable
488+
public var projectedValue: Property<Value> {
489+
return self
490+
}
491+
481492
/// A producer for Signals that will send the property's current
482493
/// value, followed by all changes over time, then complete when the
483494
/// property has deinitialized or has no further changes.
@@ -649,6 +660,7 @@ extension Property where Value: OptionalProtocol {
649660
/// A mutable property of type `Value` that allows observation of its changes.
650661
///
651662
/// Instances of this class are thread-safe.
663+
@propertyWrapper
652664
public final class MutableProperty<Value>: ComposableMutablePropertyProtocol {
653665
private let token: Lifetime.Token
654666
private let observer: Signal<Value, Never>.Observer
@@ -663,6 +675,17 @@ public final class MutableProperty<Value>: ComposableMutablePropertyProtocol {
663675
set { modify { $0 = newValue } }
664676
}
665677

678+
@inlinable
679+
public var wrappedValue: Value {
680+
get { value }
681+
set { value = newValue }
682+
}
683+
684+
@inlinable
685+
public var projectedValue: MutableProperty<Value> {
686+
return self
687+
}
688+
666689
/// The lifetime of the property.
667690
public let lifetime: Lifetime
668691

@@ -696,6 +719,14 @@ public final class MutableProperty<Value>: ComposableMutablePropertyProtocol {
696719
box = PropertyBox(initialValue)
697720
}
698721

722+
/// Initializes a mutable property that first takes on `initialValue`
723+
///
724+
/// - parameters:
725+
/// - initialValue: Starting value for the mutable property.
726+
public convenience init(wrappedValue: Value) {
727+
self.init(wrappedValue)
728+
}
729+
699730
/// Atomically replaces the contents of the variable.
700731
///
701732
/// - parameters:

Tests/ReactiveSwiftTests/PropertySpec.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,40 @@ class PropertySpec: QuickSpec {
310310
}
311311
}
312312
}
313+
314+
it("can be used as property wrapper") {
315+
struct Container {
316+
@MutableProperty var counter = 0
317+
}
318+
319+
let container = Container()
320+
expect(container.counter) == 0
321+
322+
container.counter += 1
323+
expect(container.counter) == 1
324+
325+
container.$counter.modify { $0 += 2 }
326+
expect(container.counter) == 3
327+
}
313328
}
314329

315330
describe("Property") {
331+
it("can be used as property wrapper") {
332+
struct Container {
333+
@Property var counter: Int
334+
}
335+
336+
let mutableCounter = MutableProperty(0)
337+
let container = Container(counter: Property(capturing: mutableCounter))
338+
expect(container.counter) == 0
339+
340+
mutableCounter.value = 1
341+
expect(container.counter) == 1
342+
343+
mutableCounter.modify { $0 += 2 }
344+
expect(container.counter) == 3
345+
}
346+
316347
describe("constant property") {
317348
it("should have the value given at initialization") {
318349
let constantProperty = Property(value: initialPropertyValue)

0 commit comments

Comments
 (0)