8000 [POC] Reconsider associatedProperty(), associatedObject(), rex_valueProperty() interfaces by ikesyo · Pull Request #69 · RACCommunity/Rex · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[POC] Reconsider associatedProperty(), associatedObject(), rex_valueProperty() interfaces #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions Source/Foundation/Association.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import ReactiveCocoa
/// This can be used as an alternative to `DynamicProperty` for creating strongly typed
/// bindings on Cocoa objects.
public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty<String> {
let initial: () -> String = { [weak host] _ in
host?.valueForKeyPath(keyPath.stringValue) as? String ?? ""
let initial: AnyObject -> String = { host in
host.valueForKeyPath(keyPath.stringValue) as? String ?? ""
}
let setter: String -> () = { [weak host] newValue in
host?.setValue(newValue, forKeyPath: keyPath.stringValue)
let setter: (AnyObject, String) -> () = { host, newValue in
host.setValue(newValue, forKeyPath: keyPath.stringValue)
}
return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter)
}
Expand All @@ -37,11 +37,11 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl
/// N.B. Ensure that `host` isn't strongly captured by `placeholder`, otherwise this will
/// create a retain cycle with `host` causing it to never dealloc.
public func associatedProperty<T: AnyObject>(host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty<T> {
let initial: () -> T = { [weak host] _ in
host?.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder()
let initial: AnyObject -> T = { host in
host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder()
}
let setter: T -> () = { [weak host] newValue in
host?.setValue(newValue, forKeyPath: keyPath.stringValue)
let setter: (AnyObject, T) -> () = { host, newValue in
host.setValue(newValue, forKeyPath: keyPath.stringValue)
}
return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter)
}
Expand All @@ -52,27 +52,27 @@ public func associatedProperty<T: AnyObject>(host: AnyObject, keyPath: StaticStr
///
/// This can be used as an alternative to `DynamicProperty` for creating strongly typed
/// bindings on Cocoa objects.
///
/// N.B. Ensure that `host` isn't strongly captured by `initial` or `setter`, otherwise this
/// will create a retain cycle with `host` causing it to never dealloc.
public func associatedProperty<T>(host: AnyObject, key: UnsafePointer<()>, initial: () -> T, setter: T -> ()) -> MutableProperty<T> {
return associatedObject(host, key: key) {
let property = MutableProperty(initial())
property.producer.start(Observer(next: setter))
public func associatedProperty<Host: AnyObject, T>(host: Host, key: UnsafePointer<()>, initial: Host -> T, setter: (Host, T) -> ()) -> MutableProperty<T> {
return associatedObject(host, key: key) { host in
let property = MutableProperty(initial(host))

property.producer.startWithNext { [weak host] next in
if let host = host {
setter(host, next)
}
}

return property
}
}

/// On first use attaches the object returned from `initial` to the `host` object using
/// `key` via `objc_setAssociatedObject`. On subsequent usage, returns said object via
/// `objc_getAssociatedObject`.
///
/// N.B. Ensure that `host` isn't strongly captured by `initial`, otherwise this will
/// create a retain cycle with `host` causing it to never dealloc.
public func associatedObject<T: AnyObject>(host: AnyObject, key: UnsafePointer<()>, initial: () -> T) -> T {
public func associatedObject<Host: AnyObject, T: AnyObject>(host: Host, key: UnsafePointer<()>, initial: Host -> T) -> T {
var value = objc_getAssociatedObject(host, key) as? T
if value == nil {
value = initial()
value = initial(host)
objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN)
}
return value!
Expand Down
36 changes: 0 additions & 36 deletions Source/Foundation/NSObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,40 +25,4 @@ extension NSObject {
return .empty
}
}

/// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent
/// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used
/// as key value when associating the property.
///
/// This can be used as an alternative to `DynamicProperty` for creating strongly typed
/// bindings on Cocoa objects.
public func rex_stringProperty(keyPath: StaticString) -> MutableProperty<String> {
return associatedProperty(self, keyPath: keyPath)
}

/// Attaches a `MutableProperty` relying on KVC for the initial value and subsequent
/// updates. Note that `keyPath` is a `StaticString` because it's pointer value is used
/// as key value when associating the property.
///
/// This can be used as an alternative to `DynamicProperty` for creating strongly typed
/// bindings on Cocoa objects.
///
/// N.B. Ensure that `self` isn't strongly captured by `placeholder`, otherwise this will
/// create a retain cycle causing `self` to never dealloc.
public func rex_classProperty<T: AnyObject>(keyPath: StaticString, placeholder: () -> T) -> MutableProperty<T> {
return associatedProperty(self, keyPath: keyPath, placeholder: placeholder)
}

/// Attaches a `MutableProperty` value under `key`. The property is initialized with
/// the result of `initial`. Changes on the property's producer are monitored and
/// written to `setter`.
///
/// This can be used as an alternative to `DynamicProperty` for creating strongly typed
/// bindings on Cocoa objects.
///
/// N.B. Ensure that `self` isn't strongly captured by `initial` or `setter`, otherwise this
/// will create a retain cycle causing `self` to never dealloc.
public func rex_valueProperty<T>(key: UnsafePointer<()>, _ initial: () -> T, _ setter: T -> ()) -> MutableProperty<T> {
return associatedProperty(self, key: key, initial: initial, setter: setter)
}
}
2 changes: 1 addition & 1 deletion Source/UIKit/UIBarItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import UIKit
extension UIBarItem {
/// Wraps a UIBarItem's `enabled` state in a bindable property.
public var rex_enabled: MutableProperty<Bool> {
return associatedProperty(self, key: &enabledKey, initial: { [weak self] in self?.enabled ?? true }, setter: { [weak self] in self?.enabled = $0 })
return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 })
}
}

Expand Down
2 changes: 1 addition & 1 deletion Source/UIKit/UIButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension UIButton {
/// Wraps a button's `title` text in a bindable property. Note that this only applies
/// to `UIControlState.Normal`.
public var rex_title: MutableProperty<String> {
return rex_valueProperty(&titleKey, { [weak self] in self?.titleForState(.Normal) ?? "" }, { [weak self] in self?.setTitle($0, forState: .Normal) })
return associatedProperty(self, key: &titleKey, initial: { $0.titleForState(.Normal) ?? "" }, setter: { $0.setTitle($1, forState: .Normal) })
}
}

Expand Down
6 changes: 3 additions & 3 deletions Source/UIKit/UIControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ extension UIControl {

/// Wraps a control's `enabled` state in a bindable property.
public var rex_enabled: MutableProperty<Bool> {
return rex_valueProperty(&enabledKey, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 })
return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 })
}

/// Wraps a control's `selected` state in a bindable property.
public var rex_selected: MutableProperty<Bool> {
return rex_valueProperty(&selectedKey, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 })
return associatedProperty(self, key: &selectedKey, initial: { $0.selected }, setter: { $0.selected = $1 })
}

/// Wraps a control's `highlighted` state in a bindable property.
public var rex_highlighted: MutableProperty<Bool> {
return rex_valueProperty(&highlightedKey, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 })
return associatedProperty(self, key: &highlightedKey, initial: { $0.highlighted }, setter: { $0.highlighted = $1 })
}
}

Expand Down
6 changes: 3 additions & 3 deletions Source/UIKit/UIImageView.swift
9E81
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import UIKit
extension UIImageView {
/// Wraps a imageView's `image` value in a bindable property.
public var rex_image: MutableProperty<UIImage?> {
return rex_valueProperty(&imageKey, { [weak self] in self?.image }, { [weak self] in self?.image = $0 })
return associatedProperty(self, key: &imageKey, initial: { $0.image }, setter: { $0.image = $1 })
}

/// Wraps a imageView's `highlightedImage` value in a bindable property.
public var rex_highlightedImage: MutableProperty<UIImage?> {
return rex_valueProperty(&highlightedImageKey, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 })
return associatedProperty(self, key: &highlightedImageKey, initial: { $0.highlightedImage }, setter: { $0.highlightedImage = $1 })
}
}

private var imageKey: UInt8 = 0
private var highlightedImageKey: UInt8 = 0
private var highlightedImageKey: UInt8 = 0
4 changes: 2 additions & 2 deletions Source/UIKit/UILabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import UIKit
extension UILabel {
/// Wraps a label's `text` value in a bindable property.
public var rex_text: MutableProperty<String> {
return rex_stringProperty("text")
return associatedProperty(self, keyPath: "text")
}

public var rex_textColor: MutableProperty<UIColor> {
return rex_valueProperty(&textColor, { [weak self] in self?.textColor ?? UILabel().textColor }, { [weak self] in self?.textColor = $0 })
return associatedProperty(self, key: &textColor, initial: { $0.textColor }, setter: { $0.textColor = $1 })
}
}
6 changes: 3 additions & 3 deletions Source/UIKit/UIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import UIKit
extension UIView {
/// Wraps a view's `alpha` value in a bindable property.
public var rex_alpha: MutableProperty<CGFloat> {
return rex_valueProperty(&alphaKey, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 })
return associatedProperty(self, key: &alphaKey, initial: { $0.alpha }, setter: { $0.alpha = $1 })
}

/// Wraps a view's `hidden` state in a bindable property.
public var rex_hidden: MutableProperty<Bool> {
return rex_valueProperty(&hiddenKey, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 })
return associatedProperty(self, key: &hiddenKey, initial: { $0.hidden }, setter: { $0.hidden = $1 })
}
}

private var alphaKey: UInt8 = 0
private var hiddenKey: UInt8 = 0
private var hiddenKey: UInt8 = 0
4 changes: 2 additions & 2 deletions Tests/Foundation/NSObjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ final class NSObjectDeallocTests: XCTestCase {
let object = Object()
_object = object

object.rex_stringProperty("string") <~ SignalProducer(value: "Test")
associatedProperty(object, keyPath: "string") <~ SignalProducer(value: "Test")
XCTAssert(_object?.string == "Test")
}

func testClassPropertyDoesntCreateRetainCycle() {
let object = Object()
_object = object

object.rex_classProperty("string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test")
associatedProperty(object, keyPath: "string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test")
XCTAssert(_object?.string == "Test")
}
}
Expand Down
0