DynamicProperty.swift 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import Foundation
  2. import enum Result.NoError
  3. /// Wraps a `dynamic` property, or one defined in Objective-C, using Key-Value
  4. /// Coding and Key-Value Observing.
  5. ///
  6. /// Use this class only as a last resort! `MutableProperty` is generally better
  7. /// unless KVC/KVO is required by the API you're using (for example,
  8. /// `NSOperation`).
  9. @objc public final class DynamicProperty: RACDynamicPropertySuperclass, MutablePropertyType {
  10. public typealias Value = AnyObject?
  11. private weak var object: NSObject?
  12. private let keyPath: String
  13. private var property: MutableProperty<AnyObject?>?
  14. /// The current value of the property, as read and written using Key-Value
  15. /// Coding.
  16. public var value: AnyObject? {
  17. @objc(rac_value) get {
  18. return object?.valueForKeyPath(keyPath)
  19. }
  20. @objc(setRac_value:) set(newValue) {
  21. object?.setValue(newValue, forKeyPath: keyPath)
  22. }
  23. }
  24. /// A producer that will create a Key-Value Observer for the given object,
  25. /// send its initial value then all changes over time, and then complete
  26. /// when the observed object has deallocated.
  27. ///
  28. /// - important: This only works if the object given to init() is KVO-compliant.
  29. /// Most UI controls are not!
  30. public var producer: SignalProducer<AnyObject?, NoError> {
  31. return property?.producer ?? .empty
  32. }
  33. public var signal: Signal<AnyObject?, NoError> {
  34. return property?.signal ?? .empty
  35. }
  36. /// Initializes a property that will observe and set the given key path of
  37. /// the given object.
  38. ///
  39. /// - important: `object` must support weak references!
  40. ///
  41. /// - parameters:
  42. /// - object: An object to be observed.
  43. /// - keyPath: Key path to observe on the object.
  44. public init(object: NSObject?, keyPath: String) {
  45. self.object = object
  46. self.keyPath = keyPath
  47. self.property = MutableProperty(nil)
  48. /// A DynamicProperty will stay alive as long as its object is alive.
  49. /// This is made possible by strong reference cycles.
  50. super.init()
  51. object?.rac_valuesForKeyPath(keyPath, observer: nil)?
  52. .toSignalProducer()
  53. .start { event in
  54. switch event {
  55. case let .Next(newValue):
  56. self.property?.value = newValue
  57. case let .Failed(error):
  58. fatalError("Received unexpected error from KVO signal: \(error)")
  59. case .Interrupted, .Completed:
  60. self.property = nil
  61. }
  62. }
  63. }
  64. }
  65. // MARK: Operators
  66. /// Binds a signal to a `DynamicProperty`, automatically bridging values to Objective-C.
  67. public func <~ <S: SignalType where S.Value: _ObjectiveCBridgeable, S.Error == NoError>(property: DynamicProperty, signal: S) -> Disposable {
  68. return property <~ signal.map { $0._bridgeToObjectiveC() }
  69. }
  70. /// Binds a signal producer to a `DynamicProperty`, automatically bridging values to Objective-C.
  71. public func <~ <S: SignalProducerType where S.Value: _ObjectiveCBridgeable, S.Error == NoError>(property: DynamicProperty, producer: S) -> Disposable {
  72. return property <~ producer.map { $0._bridgeToObjectiveC() }
  73. }
  74. /// Binds `destinationProperty` to the latest values of `sourceProperty`, automatically bridging values to Objective-C.
  75. public func <~ <Source: PropertyType where Source.Value: _ObjectiveCBridgeable>(destinationProperty: DynamicProperty, sourceProperty: Source) -> Disposable {
  76. return destinationProperty <~ sourceProperty.producer
  77. }