@@ -5,71 +5,70 @@ import enum Result.NoError
55/// types, including generic types when boxed via `AnyObject`).
66private protocol ObjectiveCRepresentable {
77 associatedtype Value
8- static func extract( from representation: Any ) -> Value
9- static func represent( _ value: Value ) -> Any
8+ static func extract( from representation: Any ? ) -> Value
9+ static func represent( _ value: Value ) -> Any ?
1010}
1111
12- /// Wraps a `dynamic` property, or one defined in Objective-C, using Key-Value
12+ /// A lens to a `dynamic` property, or one defined in Objective-C, using Key-Value
1313/// Coding and Key-Value Observing.
1414///
15- /// Use this class only as a last resort! `MutableProperty` is generally better
16- /// unless KVC/KVO is required by the API you're using (for example,
17- /// `NSOperation`).
15+ /// - important: `DynamicProperty` retains its lensing object. Moreover,
16+ /// `DynamicProperty` merely passes through the lifetime of its
17+ /// lensing object. Therefore, all bindings targeting a
18+ /// `DynamicProperty` would not be solely teared down by its
19+ /// deinitialization.
20+ ///
21+ /// - warning: Use this class only as a last resort. For just observations to
22+ /// KVO-compliant key paths, use `NSObject.values(forKeyPath:)`.
23+ /// `MutableProperty` is generally better unless KVC/KVO is
24+ /// required by the API you're using (for example, `NSOperation`).
1825public final class DynamicProperty < Value> : MutablePropertyProtocol {
19- private weak var object : NSObject ?
26+ private let object : NSObject
2027 private let keyPath : String
2128
22- private let extractValue : ( _ from: Any ) -> Value
23- private let represent : ( Value ) -> Any
24-
25- private var property : MutableProperty < Value ? > ?
29+ private let extractValue : ( _ from: Any ? ) -> Value
30+ private let represent : ( Value ) -> Any ?
2631
27- /// The current value of the property, as read and written using Key-Value
28- /// Coding.
29- public var value : Value ? {
32+ /// The current value of the property.
33+ public var value : Value {
3034 get {
31- return object? . value ( forKeyPath: keyPath) . map ( extractValue )
35+ return extractValue ( object. value ( forKeyPath: keyPath) )
3236 }
3337
3438 set ( newValue) {
35- object? . setValue ( newValue . map ( represent) , forKeyPath: keyPath)
39+ object. setValue ( represent ( newValue ) , forKeyPath: keyPath)
3640 }
3741 }
3842
3943 /// The lifetime of the property.
4044 public var lifetime : Lifetime {
41- return object? . rac_lifetime ?? . empty
45+ return object. rac_lifetime
4246 }
4347
44- /// A producer that will create a Key-Value Observer for the given object,
45- /// send its initial value then all changes over time, and then complete
46- /// when the observed object has deallocated.
48+ /// A producer that send the initial value then all changes over time of the
49+ /// property, and then complete when its lensing object deinitializes.
4750 ///
48- /// - important: This only works if the object given to init() is KVO-compliant.
49- /// Most UI controls are not!
50- public var producer : SignalProducer < Value ? , NoError > {
51- return ( object. map { $0. values ( forKeyPath: keyPath) } ?? . empty)
52- . map { [ extractValue = self . extractValue] in $0. map ( extractValue) }
51+ /// - important: The lensing key path must be KVO compliant.
52+ public var producer : SignalProducer < Value , NoError > {
53+ return object. values ( forKeyPath: keyPath) . map ( extractValue)
5354 }
5455
55- public lazy var signal : Signal < Value ? , NoError > = { [ unowned self] in
56+ public lazy var signal : Signal < Value , NoError > = { [ unowned self] in
5657 var signal : Signal < DynamicProperty . Value , NoError > !
5758 self . producer. startWithSignal { innerSignal, _ in signal = innerSignal }
5859 return signal
5960 } ( )
6061
61- /// Initializes a property that will observe and set the given key path of
62+ /// Initializes a property that acts as a lens to the given key path of
6263 /// the given object, using the supplied representation.
6364 ///
64- /// - important: `object` must support weak references!
65- ///
6665 /// - parameters:
67- /// - object: An object to be observed .
68- /// - keyPath: Key path to observe on the object.
66+ /// - object: An object to be lensed .
67+ /// - keyPath: Key path to lense on the object.
6968 /// - representable: A representation that bridges the values across the
7069 /// language boundary.
7170 fileprivate init < Representatable: ObjectiveCRepresentable > (
72- object: NSObject ? ,
71+ object: NSObject ,
7372 keyPath: String ,
7473 representable: Representatable . Type
7574 )
@@ -80,64 +79,118 @@ public final class DynamicProperty<Value>: MutablePropertyProtocol {
8079
8180 self . extractValue = Representatable . extract ( from: )
8281 self . represent = Representatable . represent
82+ }
8383
84- /// A DynamicProperty will stay alive as long as its object is alive.
85- /// This is made possible by strong reference cycles.
86- _ = object? . rac_lifetime. ended. observeCompleted { _ = self }
84+ @discardableResult
85+ public static func <~ < Source: SignalProtocol > ( target: DynamicProperty , signal: Source ) -> Disposable ? where Source. Value == Value , Source. Error == NoError {
86+ return signal
87+ . take ( during: target. object. rac_lifetime)
88+ . observeNext { [ weak object = target. object, represent = target. represent, keyPath = target. keyPath] value in
89+ object? . setValue ( represent ( value) , forKeyPath: keyPath)
90+ }
8791 }
8892}
8993
9094extension DynamicProperty where Value: _ObjectiveCBridgeable {
91- /// Initializes a property that will observe and set the given key path of
95+ /// Initializes a property that acts as a lens to the given key path of
9296 /// the given object, where `Value` is a value type that is bridgeable
9397 /// to Objective-C.
9498 ///
95- /// - important: `object` must support weak references!
96- ///
9799 /// - parameters:
98- /// - object: An object to be observed .
99- /// - keyPath: Key path to observe on the object.
100- public convenience init ( object: NSObject ? , keyPath: String ) {
100+ /// - object: An object to be lensed .
101+ /// - keyPath: Key path to lense on the object.
102+ public convenience init ( object: NSObject , keyPath: String ) {
101103 self . init ( object: object, keyPath: keyPath, representable: BridgeableRepresentation . self)
102104 }
103105}
104106
105107extension DynamicProperty where Value: AnyObject {
106- /// Initializes a property that will observe and set the given key path of
108+ /// Initializes a property that acts as a lens to the given key path of
107109 /// the given object, where `Value` is a reference type that can be
108110 /// represented directly in Objective-C via `AnyObject`.
109111 ///
110- /// - important: `object` must support weak references!
111- ///
112112 /// - parameters:
113- /// - object: An object to be observed .
114- /// - keyPath: Key path to observe on the object.
115- public convenience init ( object: NSObject ? , keyPath: String ) {
113+ /// - object: An object to be lensed .
114+ /// - keyPath: Key path to lense on the object.
115+ public convenience init ( object: NSObject , keyPath: String ) {
116116 self . init ( object: object, keyPath: keyPath, representable: DirectRepresentation . self)
117117 }
118118}
119119
120+ extension DynamicProperty where Value: OptionalProtocol , Value. Wrapped: _ObjectiveCBridgeable {
121+ /// Initializes a property that acts as a lens to the given key path of
122+ /// the given object, where `Value` is a value type that is bridgeable
123+ /// to Objective-C.
124+ ///
125+ /// - parameters:
126+ /// - object: An object to be lensed.
127+ /// - keyPath: Key path to lense on the object.
128+ public convenience init ( object: NSObject , keyPath: String ) {
129+ self . init ( object: object, keyPath: keyPath, representable: NullableBridgeableRepresentation . self)
130+ }
131+ }
132+
133+ extension DynamicProperty where Value: OptionalProtocol , Value. Wrapped: AnyObject {
134+ /// Initializes a property that acts as a lens to the given key path of
135+ /// the given object, where `Value` is a reference type that can be
136+ /// represented directly in Objective-C via `AnyObject`.
137+ ///
138+ /// - parameters:
139+ /// - object: An object to be lensed.
140+ /// - keyPath: Key path to lense on the object.
141+ public convenience init ( object: NSObject , keyPath: String ) {
142+ self . init ( object: object, keyPath: keyPath, representable: NullableDirectRepresentation . self)
143+ }
144+ }
145+
120146/// Represents values in Objective-C directly, via `AnyObject`.
121147private struct DirectRepresentation < Value: AnyObject > : ObjectiveCRepresentable {
122- static func extract( from representation: Any ) -> Value {
148+ static func extract( from representation: Any ? ) -> Value {
123149 return representation as! Value
124150 }
125151
126- static func represent( _ value: Value ) -> Any {
152+ static func represent( _ value: Value ) -> Any ? {
127153 return value
128154 }
129155}
130156
131157/// Represents values in Objective-C indirectly, via bridging.
132158private struct BridgeableRepresentation < Value: _ObjectiveCBridgeable > : ObjectiveCRepresentable {
133- static func extract( from representation: Any ) -> Value {
159+ static func extract( from representation: Any ? ) -> Value {
134160 let object = representation as! Value . _ObjectiveCType
135161 var result : Value ?
136162 Value . _forceBridgeFromObjectiveC ( object, result: & result)
137163 return result!
138164 }
139165
140- static func represent( _ value: Value ) -> Any {
166+ static func represent( _ value: Value ) -> Any ? {
141167 return value. _bridgeToObjectiveC ( )
142168 }
143169}
170+
171+ /// Represents nullable values in Objective-C directly, via `AnyObject`.
172+ private struct NullableDirectRepresentation < Value: OptionalProtocol > : ObjectiveCRepresentable where Value. Wrapped: AnyObject {
173+ static func extract( from representation: Any ? ) -> Value {
174+ return representation as! Value
175+ }
176+
177+ static func represent( _ value: Value ) -> Any ? {
178+ return value. optional
179+ }
180+ }
181+
182+ /// Represents nullable values in Objective-C indirectly, via bridging.
183+ private struct NullableBridgeableRepresentation < Value: OptionalProtocol > : ObjectiveCRepresentable where Value. Wrapped: _ObjectiveCBridgeable {
184+ static func extract( from representation: Any ? ) -> Value {
185+ let object = representation as? Value . Wrapped . _ObjectiveCType
186+ return Value ( reconstructing: object. map { value in
187+ var result : Value . Wrapped ?
188+ Value . Wrapped. _forceBridgeFromObjectiveC ( value, result: & result)
189+ return result!
190+ } )
191+ }
192+
193+ static func represent( _ value: Value ) -> Any ? {
194+ return value. optional. map { $0. _bridgeToObjectiveC ( ) }
195+ }
196+ }
0 commit comments