From a9c405241d932392b0f5f864417e22d48a5fe2fd Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:30:25 +0200 Subject: [PATCH 1/5] UIImageView bindable properties --- Rex.xcodeproj/project.pbxproj | 8 ++++++ Source/UIKit/UIImageView.swift | 25 ++++++++++++++++++ Tests/UIKit/UIImageViewTests.swift | 41 ++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Source/UIKit/UIImageView.swift create mode 100644 Tests/UIKit/UIImageViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 3388f01..cc4ebb4 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; + 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; settings = {ASSET_TAGS = (); }; }; 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; @@ -98,6 +100,8 @@ 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; + 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; + 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; @@ -268,6 +272,7 @@ D86FFBDC1B34B691001A89B3 /* UIButton.swift */, D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, + 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, ); path = UIKit; sourceTree = ""; @@ -279,6 +284,7 @@ 8295FD881B873490007C9000 /* UIButtonTests.swift */, 8295FD851B873081007C9000 /* UIControlTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, + 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, ); path = UIKit; sourceTree = ""; @@ -505,6 +511,7 @@ D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, + 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, @@ -521,6 +528,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift new file mode 100644 index 0000000..e522c01 --- /dev/null +++ b/Source/UIKit/UIImageView.swift @@ -0,0 +1,25 @@ +// +// UIImageView.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIImageView { + /// Wraps a imageView's `image` value in a bindable property. + public var rex_image: MutableProperty { + return rex_valueProperty(&image, { [weak self] in self?.image }, { [weak self] in self?.image = $0 }) + } + + /// Wraps a imageView's `highlightedImage` value in a bindable property. + public var rex_highlightedImage: MutableProperty { + return rex_valueProperty(&highlightedImage, { [weak self] in self?.highlightedImage }, { [weak self] in self?.highlightedImage = $0 }) + } +} + +private var image: UInt8 = 0 +private var highlightedImage: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift new file mode 100644 index 0000000..0f367bf --- /dev/null +++ b/Tests/UIKit/UIImageViewTests.swift @@ -0,0 +1,41 @@ +// +// UIImageViewTests.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIImageViewTests: XCTestCase { + + weak var _imageView: UIImageView? + + override func tearDown() { + XCTAssert(_imageView == nil, "Retain cycle detected in UIImageView properties") + super.tearDown() + } + + func testImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRectZero) + _imageView = imageView + + let image = UIImage() + + imageView.rex_image <~ SignalProducer(value: image) + XCTAssert(_imageView?.image == image) + } + + func testHighlightedImagePropertyDoesntCreateRetainCycle() { + let imageView = UIImageView(frame: CGRectZero) + _imageView = imageView + + let image = UIImage() + + imageView.rex_highlightedImage <~ SignalProducer(value: image) + XCTAssert(_imageView?.highlightedImage == image) + } +} From 0aa212e9893a8dbd4bbdea8c4059fa4b7e55a781 Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:34:56 +0200 Subject: [PATCH 2/5] UIControl.rex_selected --- Source/UIKit/UIControl.swift | 6 ++++++ Tests/UIKit/UIControlTests.swift | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index bff8b38..c0ac3eb 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -22,6 +22,12 @@ extension UIControl { public var rex_enabled: MutableProperty { return rex_valueProperty(&enabled, { [weak self] in self?.enabled ?? true }, { [weak self] in self?.enabled = $0 }) } + + /// Wraps a control's `selected` state in a bindable property. + public var rex_selected: MutableProperty { + return rex_valueProperty(&selected, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) + } } private var enabled: UInt8 = 0 +private var selected: UInt8 = 0 diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 6ffd472..a18a1ce 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -26,4 +26,12 @@ class UIControlTests: XCTestCase { control.rex_enabled <~ SignalProducer(value: false) XCTAssert(_control?.enabled == false) } + + func testSelectedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRectZero) + _control = control + + control.rex_selected <~ SignalProducer(value: false) + XCTAssert(_control?.selected == false) + } } From db5530be1eedcbcf8867d92297e63181eaaa7a5c Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:36:50 +0200 Subject: [PATCH 3/5] UIControl.rex_highlighted --- Source/UIKit/UIControl.swift | 6 ++++++ Tests/UIKit/UIControlTests.swift | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index c0ac3eb..33b44eb 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -27,7 +27,13 @@ extension UIControl { public var rex_selected: MutableProperty { return rex_valueProperty(&selected, { [weak self] in self?.selected ?? false }, { [weak self] in self?.selected = $0 }) } + + /// Wraps a control's `highlighted` state in a bindable property. + public var rex_highlighted: MutableProperty { + return rex_valueProperty(&highlighted, { [weak self] in self?.highlighted ?? false }, { [weak self] in self?.highlighted = $0 }) + } } private var enabled: UInt8 = 0 private var selected: UInt8 = 0 +private var highlighted: UInt8 = 0 diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index a18a1ce..e097598 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -34,4 +34,12 @@ class UIControlTests: XCTestCase { control.rex_selected <~ SignalProducer(value: false) XCTAssert(_control?.selected == false) } + + func testHighlightedPropertyDoesntCreateRetainCycle() { + let control = UIControl(frame: CGRectZero) + _control = control + + control.rex_highlighted <~ SignalProducer(value: true) + XCTAssert(_control?.highlighted == true) + } } From 2e499c322cd24ae9a2b398af4ebb11f90c3d1959 Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:37:24 +0200 Subject: [PATCH 4/5] update UIControl.rex_selected tests --- Tests/UIKit/UIControlTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index e097598..312c451 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -31,8 +31,8 @@ class UIControlTests: XCTestCase { let control = UIControl(frame: CGRectZero) _control = control - control.rex_selected <~ SignalProducer(value: false) - XCTAssert(_control?.selected == false) + control.rex_selected <~ SignalProducer(value: true) + XCTAssert(_control?.selected == true) } func testHighlightedPropertyDoesntCreateRetainCycle() { From 27b2a1f996f844454e919096e4678ee17ff1b32b Mon Sep 17 00:00:00 2001 From: "Andy Jacobs (REVOLVER)" Date: Wed, 21 Oct 2015 18:43:37 +0200 Subject: [PATCH 5/5] UIView.rex_alpha, UIView.rex_hidden --- Rex.xcodeproj/project.pbxproj | 8 ++++++++ Source/UIKit/UIView.swift | 25 +++++++++++++++++++++++ Tests/UIKit/UIViewTests.swift | 37 +++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 Source/UIKit/UIView.swift create mode 100644 Tests/UIKit/UIViewTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index cc4ebb4..e8922e3 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4238D5951B4D5950008534C0 /* NSTextField.swift */; }; 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */; settings = {ASSET_TAGS = (); }; }; 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E21BD7EF740097FB60 /* UIImageView.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E41BD7F6DD0097FB60 /* UIView.swift */; settings = {ASSET_TAGS = (); }; }; + 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; settings = {ASSET_TAGS = (); }; }; 8295FD871B87309F007C9000 /* UIControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD851B873081007C9000 /* UIControlTests.swift */; }; 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD881B873490007C9000 /* UIButtonTests.swift */; }; 8295FD8D1B87374A007C9000 /* UIBarButtonItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */; }; @@ -102,6 +104,8 @@ 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageViewTests.swift; sourceTree = ""; }; 8289A2E21BD7EF740097FB60 /* UIImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageView.swift; sourceTree = ""; }; + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewTests.swift; sourceTree = ""; }; 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControlTests.swift; sourceTree = ""; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonTests.swift; sourceTree = ""; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItemTests.swift; sourceTree = ""; }; @@ -273,6 +277,7 @@ D86FFBD41B34B0FE001A89B3 /* UIControl.swift */, D86FFBD71B34B242001A89B3 /* UILabel.swift */, 8289A2E21BD7EF740097FB60 /* UIImageView.swift */, + 8289A2E41BD7F6DD0097FB60 /* UIView.swift */, ); path = UIKit; sourceTree = ""; @@ -285,6 +290,7 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */, D8F073141B861B3A0047D546 /* UILabelTests.swift */, 8289A2E01BD7EF1F0097FB60 /* UIImageViewTests.swift */, + 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */, ); path = UIKit; sourceTree = ""; @@ -518,6 +524,7 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */, D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, + 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, @@ -530,6 +537,7 @@ files = ( 8289A2E11BD7EF1F0097FB60 /* UIImageViewTests.swift in Sources */, D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */, + 8289A2E81BD7F7900097FB60 /* UIViewTests.swift in Sources */, D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */, 8295FD8A1B87352D007C9000 /* UIButtonTests.swift in Sources */, D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */, diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift new file mode 100644 index 0000000..8532310 --- /dev/null +++ b/Source/UIKit/UIView.swift @@ -0,0 +1,25 @@ +// +// UIView.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit + +extension UIView { + /// Wraps a view's `alpha` value in a bindable property. + public var rex_alpha: MutableProperty { + return rex_valueProperty(&alpha, { [weak self] in self?.alpha ?? 1.0 }, { [weak self] in self?.alpha = $0 }) + } + + /// Wraps a view's `hidden` state in a bindable property. + public var rex_hidden: MutableProperty { + return rex_valueProperty(&hidden, { [weak self] in self?.hidden ?? false }, { [weak self] in self?.hidden = $0 }) + } +} + +private var alpha: UInt8 = 0 +private var hidden: UInt8 = 0 \ No newline at end of file diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift new file mode 100644 index 0000000..8873171 --- /dev/null +++ b/Tests/UIKit/UIViewTests.swift @@ -0,0 +1,37 @@ +// +// UIViewTests.swift +// Rex +// +// Created by Andy Jacobs on 21/10/15. +// Copyright © 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa +import UIKit +import XCTest + +class UIViewTests: XCTestCase { + + weak var _view: UIView? + + override func tearDown() { + XCTAssert(_view == nil, "Retain cycle detected in UIView properties") + super.tearDown() + } + + func testAlphaPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRectZero) + _view = view + + view.rex_alpha <~ SignalProducer(value: 0.5) + XCTAssert(_view?.alpha == 0.5) + } + + func testHiddenPropertyDoesntCreateRetainCycle() { + let view = UIView(frame: CGRectZero) + _view = view + + view.rex_hidden <~ SignalProducer(value: true) + XCTAssert(_view?.hidden == true) + } +} \ No newline at end of file