From dbdb5fc78d0b67e03228019e63ee3f847ed2ff25 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Thu, 7 Jan 2016 08:45:11 -0500 Subject: [PATCH 01/31] Adding a bunch of Mac-side control bindings In doing some of my own work, I found that I was duplicating a lot of what was already done in Rex, so I ported over much of what I've done on my own in order to better consolidate things. There isn't a complete 1:1 test matching at the moment with the new AppKit methods, and I "broke" the API for NSTextField that was already in place to better match with conventions used elsewhere in RAC, and to distinguish between continuous and "normal" control events. --- Rex.xcodeproj/project.pbxproj | 24 ++++++++++++++ Source/AppKit/NSControl.swift | 52 +++++++++++++++++++++++++++++++ Source/AppKit/NSPopUpButton.swift | 35 +++++++++++++++++++++ Source/AppKit/NSTextField.swift | 26 +++++++++++++--- Tests/NSControlTests.swift | 37 ++++++++++++++++++++++ Tests/NSTextFieldTests.swift | 29 +++++++++++++++++ 6 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 Source/AppKit/NSControl.swift create mode 100644 Source/AppKit/NSPopUpButton.swift create mode 100644 Tests/NSControlTests.swift create mode 100644 Tests/NSTextFieldTests.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 99dd0fc..b7a4a8b 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -15,6 +15,10 @@ 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 */; }; + 833859951C3E9B8B00EE372F /* NSControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833859941C3E9B8B00EE372F /* NSControl.swift */; }; + 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */; }; + 8338599A1C3EA15A00EE372F /* NSControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833859991C3EA15A00EE372F /* NSControlTests.swift */; }; + 8338599C1C3EA23B00EE372F /* NSTextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -175,6 +179,10 @@ 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 = ""; }; + 833859941C3E9B8B00EE372F /* NSControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSControl.swift; path = AppKit/NSControl.swift; sourceTree = ""; }; + 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSPopUpButton.swift; path = AppKit/NSPopUpButton.swift; sourceTree = ""; }; + 833859991C3EA15A00EE372F /* NSControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControlTests.swift; sourceTree = ""; }; + 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D8003E931AFEC3D400D7D3C5 /* Rex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Rex.h; sourceTree = ""; }; D8003E9F1AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -282,6 +290,17 @@ isa = PBXGroup; children = ( 4238D5951B4D5950008534C0 /* NSTextField.swift */, + 833859941C3E9B8B00EE372F /* NSControl.swift */, + 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */, + ); + name = AppKit; + sourceTree = ""; + }; + 833859981C3EA14400EE372F /* AppKit */ = { + isa = PBXGroup; + children = ( + 833859991C3EA15A00EE372F /* NSControlTests.swift */, + 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */, ); name = AppKit; sourceTree = ""; @@ -322,6 +341,7 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */ = { isa = PBXGroup; children = ( + 833859981C3EA14400EE372F /* AppKit */, D8A454081BD2772700C9E790 /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, @@ -725,11 +745,13 @@ D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, + 833859951C3E9B8B00EE372F /* NSControl.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -737,10 +759,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8338599C1C3EA23B00EE372F /* NSTextFieldTests.swift in Sources */, D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */, D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */, D8003EC31AFED30F00D7D3C5 /* SignalProducerTests.swift in Sources */, D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */, + 8338599A1C3EA15A00EE372F /* NSControlTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/AppKit/NSControl.swift b/Source/AppKit/NSControl.swift new file mode 100644 index 0000000..cc62156 --- /dev/null +++ b/Source/AppKit/NSControl.swift @@ -0,0 +1,52 @@ +// +// NSControl.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import AppKit +import ReactiveCocoa + +extension NSControl { + /// Exposes a property that binds an action to the control's target/action. The action + /// is set as a target of the control's events. For instance, an NSButton would trigger + /// its action when pressed, a NSSlider would trigger its action when the value + /// changes, a NSTextField triggers when its value changes, and so on. When property + /// changes occur the previous action is overwritten as a target. This also binds the + /// enabled state of the action to the `rex_enabled` property on the button. + public var rex_action: MutableProperty { + return associatedObject(self, key: &actionKey, initial: { [weak self] _ in + let initial = CocoaAction.rex_disabled + let property = MutableProperty(initial) + + property.producer + .startWithNext { next in + self?.target = next + self?.action = CocoaAction.selector + } + + if let strongSelf = self { + strongSelf.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } + } + + return property + }) + } + + /// Wraps a control's `enabled` state in a bindable property. + public var rex_enabled: MutableProperty { + return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 }) + } + + /// Wraps a control's alpha value in a bindable property. + public var rex_alphaValue: MutableProperty { + return associatedProperty(self, key: &alphaValueKey, initial: { $0.alphaValue }, setter: { $0.alphaValue = $1 }) + } +} + +private var enabledKey: UInt8 = 0 +private var alphaValueKey: UInt8 = 0 +private var actionKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/AppKit/NSPopUpButton.swift b/Source/AppKit/NSPopUpButton.swift new file mode 100644 index 0000000..322fdac --- /dev/null +++ b/Source/AppKit/NSPopUpButton.swift @@ -0,0 +1,35 @@ +// +// NSPopUpButton.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import AppKit +import ReactiveCocoa + +extension NSPopUpButton { + public var rex_indexOfSelectedItem: MutableProperty { + return associatedProperty(self, key: &indexOfSelectedItemKey, initial: { $0.indexOfSelectedItem }, setter: { $0.selectItemAtIndex($1) } ) + } + + public var rex_title: MutableProperty { + return associatedProperty(self, key: &titleKey, initial: { $0.title }, setter: { $0.setTitle($1) }) + } + + public var rex_attributedTitle: MutableProperty { + return associatedProperty(self, key: &attributedTitleKey, + initial: { $0.attributedTitle }, + setter: { + let menuItem = NSMenuItem(title: $1.string, action: nil, keyEquivalent: "") + menuItem.attributedTitle = $1 + ($0.cell as? NSPopUpButtonCell)?.menuItem = menuItem + }) + } +} + +private var indexOfSelectedItemKey: UInt8 = 0 +private var titleKey: UInt8 = 0 +private var attributedTitleKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index c7bf7b7..e07ce1e 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -7,16 +7,34 @@ // import Foundation -import ReactiveCocoa import AppKit +import ReactiveCocoa extension NSTextField { - /// Sends the field's string value whenever it changes. - public var rex_textSignal: SignalProducer { + /// Sends the text field's string value as it changes. Equivalent to the "continuous" + /// control setting in IB + public var rex_continuousStringValues: SignalProducer { return NSNotificationCenter.defaultCenter() .rac_notifications(NSControlTextDidChangeNotification, object: self) .map { notification in (notification.object as! NSTextField).stringValue - } + } + } + + /// Sends the text field's string value when editing completes. For instance, when the + /// user tabs out of the control, or hits the return key. + public var rex_stringValues: SignalProducer { + return NSNotificationCenter.defaultCenter() + .rac_notifications(NSControlTextDidEndEditingNotification, object: self) + .map { notification in + (notification.object as! NSTextField).stringValue + } + } + + /// Wraps a text field's text color value into a bindable property. + public var rex_textColor: MutableProperty { + return associatedProperty(self, key: &textColorKey, initial: { $0.textColor }, setter: { $0.textColor = $1 } ) } } + +private var textColorKey: UInt8 = 0 \ No newline at end of file diff --git a/Tests/NSControlTests.swift b/Tests/NSControlTests.swift new file mode 100644 index 0000000..8ed24d1 --- /dev/null +++ b/Tests/NSControlTests.swift @@ -0,0 +1,37 @@ +// +// NSControlTests.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveCocoa +import Foundation +import XCTest + +final class NSControlTests : XCTestCase { + weak var _control: NSControl? + + override func tearDown() { + XCTAssert(_control == nil, "Retain cycle detected in UIControl properties") + super.tearDown() + } + + func testEnabledPropertyDoesntCreateRetainCycle() { + let control = NSControl(frame: CGRectZero) + _control = control + + control.rex_enabled <~ SignalProducer(value: false) + XCTAssert(_control?.enabled == false) + } + + func testAlphaValuePropertyDoesntCreateRetainCycle() { + let control = NSControl(frame: CGRectZero) + _control = control + + control.rex_alphaValue <~ SignalProducer(value: 0.0) + XCTAssert(_control?.alphaValue == 0.0) + } +} \ No newline at end of file diff --git a/Tests/NSTextFieldTests.swift b/Tests/NSTextFieldTests.swift new file mode 100644 index 0000000..2557a17 --- /dev/null +++ b/Tests/NSTextFieldTests.swift @@ -0,0 +1,29 @@ +// +// NSTextFieldTests.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveCocoa +import Foundation +import XCTest + +final class NSTextFieldTests : XCTestCase { + weak var _textField: NSTextField? + + override func tearDown() { + XCTAssert(_textField == nil, "Retain cycle detected in NSTextField properties") + super.tearDown() + } + + func testTextColorPropertyDoesntCreateRetainCycle() { + let label = NSTextField(frame: CGRectZero) + _textField = label + + label.rex_textColor <~ SignalProducer(value: NSColor.greenColor()) + XCTAssert(_textField?.textColor == NSColor.greenColor()) + } +} \ No newline at end of file From 0cd6c708d0c6838b8c7409b7851d5cb0ca4421c2 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 2 Feb 2016 10:06:42 -0500 Subject: [PATCH 02/31] Adding my work on control bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally developed alongside a forthcoming update to FuzzMeasure (http://fuzzmeasure.com), this is my take on one way that Cocoa Bindings-like behavior may be approximated using RAC. In addition to the `Signal`s that contain controls' changing values, the `ConsumerBinding` provides the "other half" of the equation—updating their state to reflect incoming values. Like Cocoa Bindings, values are wrapped by placeholders that represent selection states such as `MultipleValues` and `NoSelection`. A new `PropertyBinding` is a similar concept that exists for model objects to expose, where a `validator` function is provided to allow the "massaging" of incoming values. This approximates the role of Key-Value Validation as it existed within Cocoa Bindings. This changeset does not include any examples, sadly. Hopefully I can put something together in the near future to share. --- Rex.xcodeproj/project.pbxproj | 86 +++++++++++++++ .../Bindings/AppKit/NSButton+Bindable.swift | 27 +++++ .../Bindings/AppKit/NSControl+Bindable.swift | 28 +++++ .../AppKit/NSPopUpButton+Bindable.swift | 100 ++++++++++++++++++ .../Bindings/AppKit/NSSlider+Bindable.swift | 24 +++++ .../AppKit/NSTextField+Bindable.swift | 34 ++++++ Source/Bindings/BindingValue.swift | 93 ++++++++++++++++ Source/Bindings/BindingValueConsumer.swift | 38 +++++++ Source/Bindings/ConsumerBinding.swift | 41 +++++++ Source/Bindings/PropertyBinding.swift | 73 +++++++++++++ Source/Bindings/TypedDynamicProperty.swift | 30 ++++++ 11 files changed, 574 insertions(+) create mode 100644 Source/Bindings/AppKit/NSButton+Bindable.swift create mode 100644 Source/Bindings/AppKit/NSControl+Bindable.swift create mode 100644 Source/Bindings/AppKit/NSPopUpButton+Bindable.swift create mode 100644 Source/Bindings/AppKit/NSSlider+Bindable.swift create mode 100644 Source/Bindings/AppKit/NSTextField+Bindable.swift create mode 100644 Source/Bindings/BindingValue.swift create mode 100644 Source/Bindings/BindingValueConsumer.swift create mode 100644 Source/Bindings/ConsumerBinding.swift create mode 100644 Source/Bindings/PropertyBinding.swift create mode 100644 Source/Bindings/TypedDynamicProperty.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 18ce655..12a8e60 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -19,6 +19,31 @@ 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */; }; 8338599A1C3EA15A00EE372F /* NSControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 833859991C3EA15A00EE372F /* NSControlTests.swift */; }; 8338599C1C3EA23B00EE372F /* NSTextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */; }; + 83B8FB301C60F7100075D9AF /* BindingValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */; }; + 83B8FB311C60F7100075D9AF /* BindingValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */; }; + 83B8FB321C60F7100075D9AF /* BindingValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */; }; + 83B8FB331C60F7100075D9AF /* BindingValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */; }; + 83B8FB341C60F7100075D9AF /* BindingValueConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */; }; + 83B8FB351C60F7100075D9AF /* BindingValueConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */; }; + 83B8FB361C60F7100075D9AF /* BindingValueConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */; }; + 83B8FB371C60F7100075D9AF /* BindingValueConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */; }; + 83B8FB381C60F7100075D9AF /* ConsumerBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */; }; + 83B8FB391C60F7100075D9AF /* ConsumerBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */; }; + 83B8FB3A1C60F7100075D9AF /* ConsumerBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */; }; + 83B8FB3B1C60F7100075D9AF /* ConsumerBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */; }; + 83B8FB3C1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; + 83B8FB3D1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; + 83B8FB3E1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; + 83B8FB3F1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; + 83B8FB401C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; + 83B8FB411C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; + 83B8FB421C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; + 83B8FB431C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; + 83B8FB491C60F7220075D9AF /* NSButton+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB441C60F7220075D9AF /* NSButton+Bindable.swift */; }; + 83B8FB4A1C60F7220075D9AF /* NSControl+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB451C60F7220075D9AF /* NSControl+Bindable.swift */; }; + 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */; }; + 83B8FB4C1C60F7220075D9AF /* NSSlider+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB471C60F7220075D9AF /* NSSlider+Bindable.swift */; }; + 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -186,6 +211,16 @@ 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSPopUpButton.swift; path = AppKit/NSPopUpButton.swift; sourceTree = ""; }; 833859991C3EA15A00EE372F /* NSControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSControlTests.swift; sourceTree = ""; }; 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTextFieldTests.swift; sourceTree = ""; }; + 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BindingValue.swift; path = Bindings/BindingValue.swift; sourceTree = ""; }; + 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BindingValueConsumer.swift; path = Bindings/BindingValueConsumer.swift; sourceTree = ""; }; + 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConsumerBinding.swift; path = Bindings/ConsumerBinding.swift; sourceTree = ""; }; + 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PropertyBinding.swift; path = Bindings/PropertyBinding.swift; sourceTree = ""; }; + 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypedDynamicProperty.swift; path = Bindings/TypedDynamicProperty.swift; sourceTree = ""; }; + 83B8FB441C60F7220075D9AF /* NSButton+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSButton+Bindable.swift"; path = "Bindings/AppKit/NSButton+Bindable.swift"; sourceTree = ""; }; + 83B8FB451C60F7220075D9AF /* NSControl+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSControl+Bindable.swift"; path = "Bindings/AppKit/NSControl+Bindable.swift"; sourceTree = ""; }; + 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSPopUpButton+Bindable.swift"; path = "Bindings/AppKit/NSPopUpButton+Bindable.swift"; sourceTree = ""; }; + 83B8FB471C60F7220075D9AF /* NSSlider+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSSlider+Bindable.swift"; path = "Bindings/AppKit/NSSlider+Bindable.swift"; sourceTree = ""; }; + 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSTextField+Bindable.swift"; path = "Bindings/AppKit/NSTextField+Bindable.swift"; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -310,6 +345,31 @@ name = AppKit; sourceTree = ""; }; + 83B8FB061C60F49D0075D9AF /* Bindings */ = { + isa = PBXGroup; + children = ( + 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */, + 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */, + 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */, + 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */, + 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */, + 83B8FB111C60F4C10075D9AF /* AppKit */, + ); + name = Bindings; + sourceTree = ""; + }; + 83B8FB111C60F4C10075D9AF /* AppKit */ = { + isa = PBXGroup; + children = ( + 83B8FB441C60F7220075D9AF /* NSButton+Bindable.swift */, + 83B8FB451C60F7220075D9AF /* NSControl+Bindable.swift */, + 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */, + 83B8FB471C60F7220075D9AF /* NSSlider+Bindable.swift */, + 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */, + ); + name = AppKit; + sourceTree = ""; + }; D8003E841AFEC3D400D7D3C5 = { isa = PBXGroup; children = ( @@ -326,6 +386,7 @@ D8A454051BD26A1A00C9E790 /* Property.swift */, D8003EBC1AFED01000D7D3C5 /* Signal.swift */, D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, + 83B8FB061C60F49D0075D9AF /* Bindings */, 4238D5941B4D593E008534C0 /* AppKit */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, @@ -749,16 +810,26 @@ buildActionMask = 2147483647; files = ( D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, + 83B8FB4A1C60F7220075D9AF /* NSControl+Bindable.swift in Sources */, + 83B8FB301C60F7100075D9AF /* BindingValue.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, + 83B8FB401C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, 833859951C3E9B8B00EE372F /* NSControl.swift in Sources */, + 83B8FB341C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, + 83B8FB491C60F7220075D9AF /* NSButton+Bindable.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, + 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + 83B8FB4C1C60F7220075D9AF /* NSSlider+Bindable.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, + 83B8FB3C1C60F7100075D9AF /* PropertyBinding.swift in Sources */, + 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */, + 83B8FB381C60F7100075D9AF /* ConsumerBinding.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -779,12 +850,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83B8FB311C60F7100075D9AF /* BindingValue.swift in Sources */, D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, D86FFBD21B34AD7A001A89B3 /* Association.swift in Sources */, C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */, + 83B8FB351C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + 83B8FB391C60F7100075D9AF /* ConsumerBinding.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, D8A454071BD26A1A00C9E790 /* Property.swift in Sources */, @@ -792,7 +866,9 @@ D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */, D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, + 83B8FB3D1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, + 83B8FB411C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, ); @@ -821,10 +897,15 @@ buildActionMask = 2147483647; files = ( D8715D9E1C210FF9005F4191 /* Property.swift in Sources */, + 83B8FB371C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, + 83B8FB331C60F7100075D9AF /* BindingValue.swift in Sources */, + 83B8FB3B1C60F7100075D9AF /* ConsumerBinding.swift in Sources */, D8715DA01C210FF9005F4191 /* SignalProducer.swift in Sources */, D8715DA31C21107F005F4191 /* Association.swift in Sources */, D8715DA51C21107F005F4191 /* NSObject.swift in Sources */, D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */, + 83B8FB3F1C60F7100075D9AF /* PropertyBinding.swift in Sources */, + 83B8FB431C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8715DA41C21107F005F4191 /* NSData.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, @@ -835,12 +916,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83B8FB321C60F7100075D9AF /* BindingValue.swift in Sources */, D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, C7932E841C4B41E100086F3C /* UITextField.swift in Sources */, + 83B8FB361C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */, D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */, + 83B8FB3A1C60F7100075D9AF /* ConsumerBinding.swift in Sources */, D8715DBE1C2112D6005F4191 /* Association.swift in Sources */, D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, D8715DC91C211553005F4191 /* UIControl.swift in Sources */, @@ -848,7 +932,9 @@ D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, + 83B8FB3E1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D8715DC81C211553005F4191 /* UIButton.swift in Sources */, + 83B8FB421C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, ); diff --git a/Source/Bindings/AppKit/NSButton+Bindable.swift b/Source/Bindings/AppKit/NSButton+Bindable.swift new file mode 100644 index 0000000..e02a418 --- /dev/null +++ b/Source/Bindings/AppKit/NSButton+Bindable.swift @@ -0,0 +1,27 @@ +// +// NSButton+Bindable.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2016-01-11. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSButton { + var rex_stateBinding: ConsumerBinding { + return associatedObject(self, key: &stateBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .Value(v): + button.state = v + default: + button.state = NSMixedState + } + } + } + } +} + +private var stateBindingKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/Bindings/AppKit/NSControl+Bindable.swift b/Source/Bindings/AppKit/NSControl+Bindable.swift new file mode 100644 index 0000000..1ad1bc7 --- /dev/null +++ b/Source/Bindings/AppKit/NSControl+Bindable.swift @@ -0,0 +1,28 @@ +// +// NSControl+Bindable.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/30/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSControl { + var rex_enabledBinding: ConsumerBinding { + return associatedObject(self, key: &enabledBindingKey) { control in + ConsumerBinding() { value in + switch value { + case let .Value(v): + control.enabled = v + default: + // If the enabled states are mixed, then we assume it's false + control.enabled = false + } + } + } + } +} + +private var enabledBindingKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift new file mode 100644 index 0000000..fbd1881 --- /dev/null +++ b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift @@ -0,0 +1,100 @@ +// +// NSPopUpButton+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2015-12-13. +// Copyright © 2015 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSPopUpButton { + + var rex_menuItemsBinding: ConsumerBinding<[NSMenuItem]> { + return associatedObject(self, key: &menuItemsBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .Value(v): + button.removeAllItems() + v.forEach { button.menu?.addItem($0) } + default: + button.displayPlaceholder(value) + } + } + } + } + + var rex_selectedIndexBinding: ConsumerBinding { + return associatedObject(self, key: &selectedIndexBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .Value(v): + button.selectItemAtIndex(v) + (button.cell as? NSPopUpButtonCell)?.menuItem = button.itemArray[v] + button.synchronizeTitleAndSelectedItem() + default: + button.displayPlaceholder(value) + } + } + } + } + + var rex_selectedTagBinding: ConsumerBinding { + return associatedObject(self, key: &selectedTagBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .Value(v): + button.selectItemWithTag(v) + default: + button.displayPlaceholder(value) + } + } + } + } + + var rex_menuItemBinding: ConsumerBinding { + return associatedObject(self, key: &menuItemBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .Value(v): + (button.cell as? NSPopUpButtonCell)?.menuItem = v + default: + button.displayPlaceholder(value) + } + } + } + } + + // MARK: Private API + + /// Displays the placeholder text for the provided value. + /// + /// - precondition: `placeholder` must specify a placeholder `BindingValue` + private func displayPlaceholder(placeholder: BindingValue) { + precondition(placeholder.isPlaceholder, "displayPlaceholder must be called with a placeholder value.") + + let controlSize = self.cell?.controlSize ?? .RegularControlSize + let font = NSFont.systemFontOfSize(NSFont.systemFontSizeForControlSize(controlSize)) + + let placeholder = placeholder.formatString({ _ in return "" }) + + let disabledColor: NSColor + if #available(OSX 10.10, *) { + disabledColor = NSColor.tertiaryLabelColor() + } else { + disabledColor = NSColor.blackColor().colorWithAlphaComponent(0.5) + } + + let attrs = [NSForegroundColorAttributeName : disabledColor, NSFontAttributeName : font] + + let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "") + menuItem.attributedTitle = NSAttributedString(string: placeholder, attributes: attrs) + (self.cell as? NSPopUpButtonCell)?.menuItem = menuItem + } +} + +private var selectedIndexBindingKey: UInt8 = 0 +private var selectedTagBindingKey: UInt8 = 0 +private var menuItemBindingKey: UInt8 = 0 +private var menuItemsBindingKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/Bindings/AppKit/NSSlider+Bindable.swift b/Source/Bindings/AppKit/NSSlider+Bindable.swift new file mode 100644 index 0000000..711f581 --- /dev/null +++ b/Source/Bindings/AppKit/NSSlider+Bindable.swift @@ -0,0 +1,24 @@ +// +// NSSlider+Bindable.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSSlider { + var rex_doubleValueBinding : ConsumerBinding { + return associatedObject(self, key: &doubleValueBindingKey) { slider in + ConsumerBinding() { value in + if case let .Value(v) = value { + slider.doubleValue = v + } + } + } + } +} + +private var doubleValueBindingKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/Bindings/AppKit/NSTextField+Bindable.swift b/Source/Bindings/AppKit/NSTextField+Bindable.swift new file mode 100644 index 0000000..ca333cf --- /dev/null +++ b/Source/Bindings/AppKit/NSTextField+Bindable.swift @@ -0,0 +1,34 @@ +// +// NSTextField+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2015-12-13. +// Copyright © 2015 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSTextField { + var rex_stringValueBinding: ConsumerBinding { + return associatedObject(self, key: &stringValueBindingKey) { textField in + ConsumerBinding() { value in + switch value { + case let .Value(v): + textField.stringValue = v + default: + // The placeholderString is only shown if the string value is cleared on the control + if #available(OSX 10.10, *) { + textField.stringValue = "" + textField.placeholderString = value.formatString({ _ in return "" }) + } else { + // TODO: Below 10.10, we must also manipulate the string color/etc. + textField.stringValue = value.formatString({ _ in return "" }) + } + } + } + } + } +} + +private var stringValueBindingKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/Bindings/BindingValue.swift b/Source/Bindings/BindingValue.swift new file mode 100644 index 0000000..3af5b8d --- /dev/null +++ b/Source/Bindings/BindingValue.swift @@ -0,0 +1,93 @@ +// +// BindingValue.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2015-12-12. +// Copyright © 2015 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation + +/// Represents a value to be bound to a UI control, including common placeholders for empty or multiple selections. +public enum BindingValue { + /// A placeholder to represent the "No Selection" case + case NoSelection + + /// A placeholder to represent the "Multiple Values" case + case MultipleValues + + /// The value to be displayed by the control + case Value(ValueType) + + /// Map the value to another (possibly differently typed) value using the supplied closure + public func map(transform: ((ValueType)->U)) -> BindingValue { + switch self { + case let .Value(v): + return .Value(transform(v)) + case .NoSelection: + return .NoSelection + case .MultipleValues: + return .MultipleValues + } + } + + /// Format the value's string using the specified formatter. (Note that you can also use `map` to achieve the same thing.) + public func formatString(formatter: (ValueType) -> String) -> String { + switch self { + case .Value(let v): + return formatter(v) + default: + return description + } + } + + /// Returns whether this `BindingValue` instance represents a placeholder + public var isPlaceholder: Bool { + switch self { + case .Value: + return false + default: + return true + } + } +} + +extension BindingValue : CustomStringConvertible { + public var description: String { + switch self { + case .Value(let v): + if let printableValue = v as? CustomStringConvertible { + return String.localizedStringWithFormat(NSLocalizedString("Value(%@)", comment: "Debug value display"), printableValue.description) + } + else { + return NSLocalizedString("Value(no description)", comment: "Debug value display for non-CustomStringConvertible type") + } + case .NoSelection: + return NSLocalizedString("No Selection", comment: "No selection placeholder") + case .MultipleValues: + return NSLocalizedString("Multiple Values", comment: "Multiple values placeholder") + } + } +} + +public extension BindingValue where ValueType : Equatable { + /// Initializes a binding value with the default behavior for arrays of values. That is, an empty array becomes .NoSelection, an array with more than one non-matching element is .MultipleValues, and the .Value itself otherwise. + public init(values: [ValueType]) { + if values.isEmpty { + self = BindingValue.NoSelection + } + else if values.count == 1 { + self = BindingValue.Value(values.first!) + } + else { + let firstValue = values.first! + let allEqual = values.suffixFrom(1).reduce(true) { $0 && ($1 == firstValue) } + if allEqual { + self = BindingValue.Value(firstValue) + } + else { + self = BindingValue.MultipleValues + } + } + } +} \ No newline at end of file diff --git a/Source/Bindings/BindingValueConsumer.swift b/Source/Bindings/BindingValueConsumer.swift new file mode 100644 index 0000000..1b8b098 --- /dev/null +++ b/Source/Bindings/BindingValueConsumer.swift @@ -0,0 +1,38 @@ +// +// BindingValueConsumer.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/6/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +/// A consumer of `BindingValue`s +protocol BindingValueConsumer { + typealias Value + typealias Error: ErrorType + + /// Bind the object to a producer of `BindingValue`s + func bindToProducer(producer: SignalProducer, Error>) + func bindToSignal(signal: Signal, Error>) +} + +func <~(control: Control, producer: SignalProducer) { + control.bindToProducer(producer.map { BindingValue.Value($0) }) +} + +func <~(control: Control, signal: Signal) { + control.bindToSignal(signal.map { BindingValue.Value($0) }) +} + +/// Connect a producer of arrays of values (i.e. a selection, or many-to-one binding) to a `BindingValueConsumer` +func <~(control: Control, producer: SignalProducer<[Value], Error>) { + control.bindToProducer(producer.map { BindingValue(values: $0) }) +} + +/// Connect a producer of `BindingValue` objects to a `BindingValueConsumer`. This is useful when you supply your own custom mapping from an array of objects to a `BindingValue`. +func <~(control: Control, producer: SignalProducer, Error>) { + control.bindToProducer(producer) +} \ No newline at end of file diff --git a/Source/Bindings/ConsumerBinding.swift b/Source/Bindings/ConsumerBinding.swift new file mode 100644 index 0000000..09abaf5 --- /dev/null +++ b/Source/Bindings/ConsumerBinding.swift @@ -0,0 +1,41 @@ +// +// ConsumerBinding.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2016-01-09. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +/// The `ConsumerBinding` represents a single point where values can be "bound" to a UI object that consumes the specified `Value` type and executes control-specific behavior upon receipt of those values. +/// +/// In practice, there can be more than one type of binding that may be exposed by a given object. For example, an `NSPopUpButton` can accept bindings to its selectedIndex, its selectedTag, its displayed and selected menuItem, etc. It must respond differently to each of those bindings. +public final class ConsumerBinding { + private let _handler: BindingValue -> Void + private let _disposable: CompositeDisposable + + /// Initialize the `ConsumerBinding` with the given behavior + /// - param handler: Called whenever a new `BindingValue` is received, the handler is called on the UI thread and can configure the UI control to display the value. + public init(handler: (BindingValue) -> Void) { + _handler = handler + _disposable = CompositeDisposable() + } +} + +extension ConsumerBinding : BindingValueConsumer { + /// Binds the specified producer to this instance + public func bindToProducer(producer: SignalProducer, NoError>) { + _disposable += producer + .observeOn(UIScheduler()) // Assert the handlers happen on the UI thread + .startWithNext(_handler) + } + + /// Binds the specified signal to this instance + public func bindToSignal(signal: Signal, NoError>) { + _disposable += signal + .observeOn(UIScheduler()) // Assert the handlers happen on the UI thread + .observeNext(_handler) + } +} \ No newline at end of file diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift new file mode 100644 index 0000000..6cadb75 --- /dev/null +++ b/Source/Bindings/PropertyBinding.swift @@ -0,0 +1,73 @@ +// +// PropertyBinding.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/6/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +/// A `PropertyBinding` matches up a property with a function that validates incoming values to the property, and wraps the two into a new `MutablePropertyType` that can be treated as its own property, and bound to. +public final class PropertyBinding { + + /// Validators are expected to return either `nil` if the input value is invalid, the passed-in value, or a corrected version of the passed-in value. The behavior is completely up to the implementor. + public typealias Validator = Property.Value -> Property.Value? + + private let _property: Property + private let _validator: Validator? + + public init(property: Property, validator: Validator?) { + _property = property + _validator = validator + } +} + +extension PropertyBinding : MutablePropertyType { + public var producer: SignalProducer { + return _property.producer + } + + public var signal: Signal { + return _property.signal + } + + public var value: Property.Value { + get { return _property.value } + set { _property.value = (_validator == nil) ? newValue : (_validator?(newValue) ?? _property.value) } + } +} + +public extension TypedDynamicProperty { + public typealias Validator = Value -> Value? + public typealias Binding = PropertyBinding> + + /// Returns a binding for the specified `object` and `keyPath`, with an optional `validator` that returns a valid, replacement value, or nil if the value is invalid. + public class func bindingForObject(object: NSObject, withKeyPath keyPath: String, validator: Validator?) -> Binding { + return PropertyBinding(property: TypedDynamicProperty(object: object, keyPath: keyPath), validator: validator) + } + + /// Wraps this instance in a `PropertyBinding` object, using the specified validator function. + /// + /// Example: + /// + /// ``` + /// final class Volume { + /// dynamic var volume: Double // Between 0 and 1. Could be defined in an ObjC parent class. + /// + /// var rex_volume: TypedDynamicProperty.Binding { + /// return associatedObject(self, key: &durationKey) { + /// TypedDynamicProperty(object: $0, keyPath: "volume").bindingWithValidator($0.validVolume) + /// } + /// } + /// + /// private func validVolume(newVolume: Double) -> Double? { + /// return max(0.0, min(1.0, newVolume)) + /// } + /// } + /// ``` + public func bindingWithValidator(validator: Validator?) -> Binding { + return PropertyBinding(property: self, validator: validator) + } +} diff --git a/Source/Bindings/TypedDynamicProperty.swift b/Source/Bindings/TypedDynamicProperty.swift new file mode 100644 index 0000000..93ee180 --- /dev/null +++ b/Source/Bindings/TypedDynamicProperty.swift @@ -0,0 +1,30 @@ +// +// TypedDynamicProperty.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +/// Wraps a `DynamicProperty` instance in an effort to handle the casting that is often required when bridging from the KVO-powered class. +public final class TypedDynamicProperty : MutablePropertyType { + private let _property: DynamicProperty + private init(_ property: DynamicProperty) { + _property = property + } + + public convenience init(object: NSObject, keyPath: String) { + self.init(DynamicProperty(object: object, keyPath: keyPath)) + } + + public var value: Value { + get { return _property.value as! Value } + set { _property.value = newValue as? AnyObject } + } + + public var producer: SignalProducer { return _property.producer.map { $0 as! Value } } + public var signal: Signal { return _property.signal.map { $0 as! Value } } +} \ No newline at end of file From ae9def8a25a2f42ffe31acf997f1a8335efe9a41 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 2 Feb 2016 10:35:51 -0500 Subject: [PATCH 03/31] Adding more NSControl extensions for Mac Trying to use Action-based triggers for values, in general, as NSControls have always worked on a target/action-based system. --- Rex.xcodeproj/project.pbxproj | 8 +++++++ Source/AppKit/NSButton.swift | 27 +++++++++++++++++++++ Source/AppKit/NSPopUpButton.swift | 28 +++++++++++++++++++++- Source/AppKit/NSSlider.swift | 26 +++++++++++++++++++++ Source/AppKit/NSViewController.swift | 35 ++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 Source/AppKit/NSButton.swift create mode 100644 Source/AppKit/NSSlider.swift create mode 100644 Source/AppKit/NSViewController.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 12a8e60..a7d88b5 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */; }; 83B8FB4C1C60F7220075D9AF /* NSSlider+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB471C60F7220075D9AF /* NSSlider+Bindable.swift */; }; 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */; }; + 83B8FB531C6102F10075D9AF /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */; }; + 83B8FB551C6102F10075D9AF /* NSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB511C6102F10075D9AF /* NSViewController.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -221,6 +223,8 @@ 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSPopUpButton+Bindable.swift"; path = "Bindings/AppKit/NSPopUpButton+Bindable.swift"; sourceTree = ""; }; 83B8FB471C60F7220075D9AF /* NSSlider+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSSlider+Bindable.swift"; path = "Bindings/AppKit/NSSlider+Bindable.swift"; sourceTree = ""; }; 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSTextField+Bindable.swift"; path = "Bindings/AppKit/NSTextField+Bindable.swift"; sourceTree = ""; }; + 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSSlider.swift; path = AppKit/NSSlider.swift; sourceTree = ""; }; + 83B8FB511C6102F10075D9AF /* NSViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSViewController.swift; path = AppKit/NSViewController.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -329,6 +333,8 @@ 4238D5941B4D593E008534C0 /* AppKit */ = { isa = PBXGroup; children = ( + 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */, + 83B8FB511C6102F10075D9AF /* NSViewController.swift */, 4238D5951B4D5950008534C0 /* NSTextField.swift */, 833859941C3E9B8B00EE372F /* NSControl.swift */, 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */, @@ -822,7 +828,9 @@ D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */, + 83B8FB551C6102F10075D9AF /* NSViewController.swift in Sources */, D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + 83B8FB531C6102F10075D9AF /* NSSlider.swift in Sources */, 83B8FB4C1C60F7220075D9AF /* NSSlider+Bindable.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, 83B8FB3C1C60F7100075D9AF /* PropertyBinding.swift in Sources */, diff --git a/Source/AppKit/NSButton.swift b/Source/AppKit/NSButton.swift new file mode 100644 index 0000000..8a47bb5 --- /dev/null +++ b/Source/AppKit/NSButton.swift @@ -0,0 +1,27 @@ +// +// NSButton+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2016-01-11. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa +import Rex + +extension NSButton { + var rex_stateAction: Action { + return associatedObject(self, key: &stateActionKey) { _ in Action { SignalProducer(value: $0.state) } } + } + + var rex_states: Signal { + let cocoaAction = associatedObject(self, key: &stateCocoaActionKey) { CocoaAction($0.rex_stateAction) { $0 as! NSButton } } + rex_action.value = cocoaAction + + return rex_stateAction.values + } +} + +private var stateActionKey: UInt8 = 0 +private var stateCocoaActionKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/AppKit/NSPopUpButton.swift b/Source/AppKit/NSPopUpButton.swift index 322fdac..927abf0 100644 --- a/Source/AppKit/NSPopUpButton.swift +++ b/Source/AppKit/NSPopUpButton.swift @@ -28,8 +28,34 @@ extension NSPopUpButton { ($0.cell as? NSPopUpButtonCell)?.menuItem = menuItem }) } + + var rex_selectedIndexAction: Action { + return associatedObject(self, key: &selectedIndexActionKey) { _ in Action { SignalProducer(value: $0.indexOfSelectedItem) } } + } + + var rex_selectedIndexes: Signal { + let cocoaAction = associatedObject(self, key: &selectedIndexKey) { CocoaAction($0.rex_selectedIndexAction) { $0 as! NSPopUpButton } } + rex_action.value = cocoaAction + + return rex_selectedIndexAction.values + } + + var rex_selectedTagAction: Action { + return associatedObject(self, key: &selectedTagActionKey) { _ in Action { SignalProducer(value: $0.selectedTag()) } } + } + + var rex_selectedTags: Signal { + let cocoaAction = associatedObject(self, key: &selectedTagKey) { CocoaAction($0.rex_selectedTagAction) { $0 as! NSPopUpButton } } + rex_action.value = cocoaAction + + return rex_selectedTagAction.values + } } private var indexOfSelectedItemKey: UInt8 = 0 private var titleKey: UInt8 = 0 -private var attributedTitleKey: UInt8 = 0 \ No newline at end of file +private var attributedTitleKey: UInt8 = 0 +private var selectedIndexKey: UInt8 = 0 +private var selectedIndexActionKey: UInt8 = 0 +private var selectedTagKey: UInt8 = 0 +private var selectedTagActionKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/AppKit/NSSlider.swift b/Source/AppKit/NSSlider.swift new file mode 100644 index 0000000..82c55b3 --- /dev/null +++ b/Source/AppKit/NSSlider.swift @@ -0,0 +1,26 @@ +// +// NSSlider+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/6/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSSlider { + var rex_doubleValueAction: Action { + return associatedObject(self, key: &doubleValueActionKey) { _ in Action { SignalProducer(value: $0.doubleValue) } } + } + + var rex_doubleValues: Signal { + let cocoaAction = associatedObject(self, key: &doubleValueCocoaActionKey) { CocoaAction($0.rex_doubleValueAction) { $0 as! NSSlider } } + rex_action.value = cocoaAction + + return rex_doubleValueAction.values + } +} + +private var doubleValueActionKey: UInt8 = 0 +private var doubleValueCocoaActionKey: UInt8 = 0 \ No newline at end of file diff --git a/Source/AppKit/NSViewController.swift b/Source/AppKit/NSViewController.swift new file mode 100644 index 0000000..0c5a6da --- /dev/null +++ b/Source/AppKit/NSViewController.swift @@ -0,0 +1,35 @@ +// +// NSViewController+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/30/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveCocoa + +extension NSViewController { + + /// Provides a "trigger signal" to allow us easier access to viewWillDisappear calls + var rex_viewWillDisappear: Signal { + return associatedObject(self, key: &viewWillDisappearKey) { + var returnedSignal: Signal! + + $0.rac_signalForSelector(Selector("viewWillDisappear")) + .toSignalProducer() + .map { _ in () } + .flatMapError { error in + let str = "Unexpected error: \(error)" // Needs to be a separate `let` to appease the compiler + fatalError(str) // Don't silently ignore errors. + } + .startWithSignal { theSignal, _ in + returnedSignal = theSignal + } + + return returnedSignal + } + } +} + +private var viewWillDisappearKey: UInt8 = 0 \ No newline at end of file From 49257a6b8d46c8aca272c244b83c812b2448bd6b Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 2 Feb 2016 11:28:45 -0500 Subject: [PATCH 04/31] Public most of the things! The joy of importing code from an app into a framework is all the internal -> public promotion... --- Source/AppKit/NSPopUpButton.swift | 8 ++++---- Source/AppKit/NSSlider.swift | 4 ++-- Source/AppKit/NSViewController.swift | 2 +- Source/Bindings/AppKit/NSButton+Bindable.swift | 2 +- Source/Bindings/AppKit/NSControl+Bindable.swift | 2 +- Source/Bindings/AppKit/NSPopUpButton+Bindable.swift | 6 +++--- Source/Bindings/AppKit/NSSlider+Bindable.swift | 2 +- Source/Bindings/AppKit/NSTextField+Bindable.swift | 2 +- Source/Bindings/BindingValueConsumer.swift | 10 +++++----- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/AppKit/NSPopUpButton.swift b/Source/AppKit/NSPopUpButton.swift index 927abf0..b953a52 100644 --- a/Source/AppKit/NSPopUpButton.swift +++ b/Source/AppKit/NSPopUpButton.swift @@ -29,22 +29,22 @@ extension NSPopUpButton { }) } - var rex_selectedIndexAction: Action { + public var rex_selectedIndexAction: Action { return associatedObject(self, key: &selectedIndexActionKey) { _ in Action { SignalProducer(value: $0.indexOfSelectedItem) } } } - var rex_selectedIndexes: Signal { + public var rex_selectedIndexes: Signal { let cocoaAction = associatedObject(self, key: &selectedIndexKey) { CocoaAction($0.rex_selectedIndexAction) { $0 as! NSPopUpButton } } rex_action.value = cocoaAction return rex_selectedIndexAction.values } - var rex_selectedTagAction: Action { + public var rex_selectedTagAction: Action { return associatedObject(self, key: &selectedTagActionKey) { _ in Action { SignalProducer(value: $0.selectedTag()) } } } - var rex_selectedTags: Signal { + public var rex_selectedTags: Signal { let cocoaAction = associatedObject(self, key: &selectedTagKey) { CocoaAction($0.rex_selectedTagAction) { $0 as! NSPopUpButton } } rex_action.value = cocoaAction diff --git a/Source/AppKit/NSSlider.swift b/Source/AppKit/NSSlider.swift index 82c55b3..43dca7f 100644 --- a/Source/AppKit/NSSlider.swift +++ b/Source/AppKit/NSSlider.swift @@ -10,11 +10,11 @@ import Foundation import ReactiveCocoa extension NSSlider { - var rex_doubleValueAction: Action { + public var rex_doubleValueAction: Action { return associatedObject(self, key: &doubleValueActionKey) { _ in Action { SignalProducer(value: $0.doubleValue) } } } - var rex_doubleValues: Signal { + public var rex_doubleValues: Signal { let cocoaAction = associatedObject(self, key: &doubleValueCocoaActionKey) { CocoaAction($0.rex_doubleValueAction) { $0 as! NSSlider } } rex_action.value = cocoaAction diff --git a/Source/AppKit/NSViewController.swift b/Source/AppKit/NSViewController.swift index 0c5a6da..64cab7d 100644 --- a/Source/AppKit/NSViewController.swift +++ b/Source/AppKit/NSViewController.swift @@ -12,7 +12,7 @@ import ReactiveCocoa extension NSViewController { /// Provides a "trigger signal" to allow us easier access to viewWillDisappear calls - var rex_viewWillDisappear: Signal { + public var rex_viewWillDisappear: Signal { return associatedObject(self, key: &viewWillDisappearKey) { var returnedSignal: Signal! diff --git a/Source/Bindings/AppKit/NSButton+Bindable.swift b/Source/Bindings/AppKit/NSButton+Bindable.swift index e02a418..557a876 100644 --- a/Source/Bindings/AppKit/NSButton+Bindable.swift +++ b/Source/Bindings/AppKit/NSButton+Bindable.swift @@ -10,7 +10,7 @@ import Foundation import ReactiveCocoa extension NSButton { - var rex_stateBinding: ConsumerBinding { + public var rex_stateBinding: ConsumerBinding { return associatedObject(self, key: &stateBindingKey) { button in ConsumerBinding() { value in switch value { diff --git a/Source/Bindings/AppKit/NSControl+Bindable.swift b/Source/Bindings/AppKit/NSControl+Bindable.swift index 1ad1bc7..2d69bf0 100644 --- a/Source/Bindings/AppKit/NSControl+Bindable.swift +++ b/Source/Bindings/AppKit/NSControl+Bindable.swift @@ -10,7 +10,7 @@ import Foundation import ReactiveCocoa extension NSControl { - var rex_enabledBinding: ConsumerBinding { + public var rex_enabledBinding: ConsumerBinding { return associatedObject(self, key: &enabledBindingKey) { control in ConsumerBinding() { value in switch value { diff --git a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift index fbd1881..d3ee6c6 100644 --- a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift +++ b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift @@ -11,7 +11,7 @@ import ReactiveCocoa extension NSPopUpButton { - var rex_menuItemsBinding: ConsumerBinding<[NSMenuItem]> { + public var rex_menuItemsBinding: ConsumerBinding<[NSMenuItem]> { return associatedObject(self, key: &menuItemsBindingKey) { button in ConsumerBinding() { value in switch value { @@ -25,7 +25,7 @@ extension NSPopUpButton { } } - var rex_selectedIndexBinding: ConsumerBinding { + public var rex_selectedIndexBinding: ConsumerBinding { return associatedObject(self, key: &selectedIndexBindingKey) { button in ConsumerBinding() { value in switch value { @@ -40,7 +40,7 @@ extension NSPopUpButton { } } - var rex_selectedTagBinding: ConsumerBinding { + public var rex_selectedTagBinding: ConsumerBinding { return associatedObject(self, key: &selectedTagBindingKey) { button in ConsumerBinding() { value in switch value { diff --git a/Source/Bindings/AppKit/NSSlider+Bindable.swift b/Source/Bindings/AppKit/NSSlider+Bindable.swift index 711f581..df2dc55 100644 --- a/Source/Bindings/AppKit/NSSlider+Bindable.swift +++ b/Source/Bindings/AppKit/NSSlider+Bindable.swift @@ -10,7 +10,7 @@ import Foundation import ReactiveCocoa extension NSSlider { - var rex_doubleValueBinding : ConsumerBinding { + public var rex_doubleValueBinding : ConsumerBinding { return associatedObject(self, key: &doubleValueBindingKey) { slider in ConsumerBinding() { value in if case let .Value(v) = value { diff --git a/Source/Bindings/AppKit/NSTextField+Bindable.swift b/Source/Bindings/AppKit/NSTextField+Bindable.swift index ca333cf..dff1c95 100644 --- a/Source/Bindings/AppKit/NSTextField+Bindable.swift +++ b/Source/Bindings/AppKit/NSTextField+Bindable.swift @@ -10,7 +10,7 @@ import Foundation import ReactiveCocoa extension NSTextField { - var rex_stringValueBinding: ConsumerBinding { + public var rex_stringValueBinding: ConsumerBinding { return associatedObject(self, key: &stringValueBindingKey) { textField in ConsumerBinding() { value in switch value { diff --git a/Source/Bindings/BindingValueConsumer.swift b/Source/Bindings/BindingValueConsumer.swift index 1b8b098..0c66afe 100644 --- a/Source/Bindings/BindingValueConsumer.swift +++ b/Source/Bindings/BindingValueConsumer.swift @@ -10,7 +10,7 @@ import Foundation import ReactiveCocoa /// A consumer of `BindingValue`s -protocol BindingValueConsumer { +public protocol BindingValueConsumer { typealias Value typealias Error: ErrorType @@ -19,20 +19,20 @@ protocol BindingValueConsumer { func bindToSignal(signal: Signal, Error>) } -func <~(control: Control, producer: SignalProducer) { +public func <~(control: Control, producer: SignalProducer) { control.bindToProducer(producer.map { BindingValue.Value($0) }) } -func <~(control: Control, signal: Signal) { +public func <~(control: Control, signal: Signal) { control.bindToSignal(signal.map { BindingValue.Value($0) }) } /// Connect a producer of arrays of values (i.e. a selection, or many-to-one binding) to a `BindingValueConsumer` -func <~(control: Control, producer: SignalProducer<[Value], Error>) { +public func <~(control: Control, producer: SignalProducer<[Value], Error>) { control.bindToProducer(producer.map { BindingValue(values: $0) }) } /// Connect a producer of `BindingValue` objects to a `BindingValueConsumer`. This is useful when you supply your own custom mapping from an array of objects to a `BindingValue`. -func <~(control: Control, producer: SignalProducer, Error>) { +public func <~(control: Control, producer: SignalProducer, Error>) { control.bindToProducer(producer) } \ No newline at end of file From 4317b1a37e505481943a155cc44206f7de881c8c Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 2 Feb 2016 11:38:26 -0500 Subject: [PATCH 05/31] Forgot to include a source file --- Rex.xcodeproj/project.pbxproj | 4 ++++ Source/AppKit/NSButton.swift | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a7d88b5..6f13515 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */; }; 83B8FB531C6102F10075D9AF /* NSSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */; }; 83B8FB551C6102F10075D9AF /* NSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB511C6102F10075D9AF /* NSViewController.swift */; }; + 83B8FB571C6112EA0075D9AF /* NSButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB561C6112EA0075D9AF /* NSButton.swift */; }; C7932E831C4B3F3000086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E841C4B41E100086F3C /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E811C4B3EDB00086F3C /* UITextField.swift */; }; C7932E871C4B42F500086F3C /* UITextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7932E851C4B420A00086F3C /* UITextFieldTests.swift */; }; @@ -225,6 +226,7 @@ 83B8FB481C60F7220075D9AF /* NSTextField+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSTextField+Bindable.swift"; path = "Bindings/AppKit/NSTextField+Bindable.swift"; sourceTree = ""; }; 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSSlider.swift; path = AppKit/NSSlider.swift; sourceTree = ""; }; 83B8FB511C6102F10075D9AF /* NSViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSViewController.swift; path = AppKit/NSViewController.swift; sourceTree = ""; }; + 83B8FB561C6112EA0075D9AF /* NSButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSButton.swift; path = AppKit/NSButton.swift; sourceTree = ""; }; C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextFieldTests.swift; sourceTree = ""; }; D8003E921AFEC3D400D7D3C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -333,6 +335,7 @@ 4238D5941B4D593E008534C0 /* AppKit */ = { isa = PBXGroup; children = ( + 83B8FB561C6112EA0075D9AF /* NSButton.swift */, 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */, 83B8FB511C6102F10075D9AF /* NSViewController.swift */, 4238D5951B4D5950008534C0 /* NSTextField.swift */, @@ -837,6 +840,7 @@ 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */, D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */, + 83B8FB571C6112EA0075D9AF /* NSButton.swift in Sources */, 83B8FB381C60F7100075D9AF /* ConsumerBinding.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Source/AppKit/NSButton.swift b/Source/AppKit/NSButton.swift index 8a47bb5..3c75654 100644 --- a/Source/AppKit/NSButton.swift +++ b/Source/AppKit/NSButton.swift @@ -8,14 +8,13 @@ import Foundation import ReactiveCocoa -import Rex extension NSButton { - var rex_stateAction: Action { + public var rex_stateAction: Action { return associatedObject(self, key: &stateActionKey) { _ in Action { SignalProducer(value: $0.state) } } } - var rex_states: Signal { + public var rex_states: Signal { let cocoaAction = associatedObject(self, key: &stateCocoaActionKey) { CocoaAction($0.rex_stateAction) { $0 as! NSButton } } rex_action.value = cocoaAction From 20a789008c43b2826a339180a601a9c52ef803cb Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Tue, 2 Feb 2016 11:45:11 -0500 Subject: [PATCH 06/31] Missed another public var --- Source/Bindings/AppKit/NSPopUpButton+Bindable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift index d3ee6c6..2731b51 100644 --- a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift +++ b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift @@ -53,7 +53,7 @@ extension NSPopUpButton { } } - var rex_menuItemBinding: ConsumerBinding { + public var rex_menuItemBinding: ConsumerBinding { return associatedObject(self, key: &menuItemBindingKey) { button in ConsumerBinding() { value in switch value { From 7f3012a9e706d6952c7fde79010861eeb293ee80 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Wed, 23 Mar 2016 13:29:33 -0400 Subject: [PATCH 07/31] Fix compilation with latest RAC, Xcode 7.3 --- Cartfile.resolved | 4 ++-- Source/Action.swift | 1 + Source/AppKit/NSButton.swift | 1 + Source/AppKit/NSControl.swift | 1 + Source/AppKit/NSPopUpButton.swift | 1 + Source/AppKit/NSSlider.swift | 1 + Source/AppKit/NSTextField.swift | 1 + Source/AppKit/NSViewController.swift | 4 +++- Source/Bindings/BindingValueConsumer.swift | 4 ++-- Source/Bindings/ConsumerBinding.swift | 1 + Source/Bindings/PropertyBinding.swift | 1 + Source/Bindings/TypedDynamicProperty.swift | 1 + Source/Foundation/NSObject.swift | 1 + Source/Foundation/NSUserDefaults.swift | 1 + Source/Property.swift | 1 + Source/Signal.swift | 1 + Source/SignalProducer.swift | 1 + Source/UIKit/UIControl.swift | 1 + Source/UIKit/UITextField.swift | 1 + 19 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 8c6e502..03fee97 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "1.0.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.0.0" +github "antitypical/Result" "1.0.2" +github "ReactiveCocoa/ReactiveCocoa" "v4.0.1" diff --git a/Source/Action.swift b/Source/Action.swift index 9dda337..3c14dd7 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import Result extension Action { /// Creates an always disabled action. diff --git a/Source/AppKit/NSButton.swift b/Source/AppKit/NSButton.swift index 3c75654..cec3deb 100644 --- a/Source/AppKit/NSButton.swift +++ b/Source/AppKit/NSButton.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result extension NSButton { public var rex_stateAction: Action { diff --git a/Source/AppKit/NSControl.swift b/Source/AppKit/NSControl.swift index cc62156..aa816e4 100644 --- a/Source/AppKit/NSControl.swift +++ b/Source/AppKit/NSControl.swift @@ -9,6 +9,7 @@ import Foundation import AppKit import ReactiveCocoa +import Result extension NSControl { /// Exposes a property that binds an action to the control's target/action. The action diff --git a/Source/AppKit/NSPopUpButton.swift b/Source/AppKit/NSPopUpButton.swift index b953a52..6d7d128 100644 --- a/Source/AppKit/NSPopUpButton.swift +++ b/Source/AppKit/NSPopUpButton.swift @@ -9,6 +9,7 @@ import Foundation import AppKit import ReactiveCocoa +import Result extension NSPopUpButton { public var rex_indexOfSelectedItem: MutableProperty { diff --git a/Source/AppKit/NSSlider.swift b/Source/AppKit/NSSlider.swift index 43dca7f..97ee09d 100644 --- a/Source/AppKit/NSSlider.swift +++ b/Source/AppKit/NSSlider.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result extension NSSlider { public var rex_doubleValueAction: Action { diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index e07ce1e..2fefb0f 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -9,6 +9,7 @@ import Foundation import AppKit import ReactiveCocoa +import Result extension NSTextField { /// Sends the text field's string value as it changes. Equivalent to the "continuous" diff --git a/Source/AppKit/NSViewController.swift b/Source/AppKit/NSViewController.swift index 64cab7d..7dc21c7 100644 --- a/Source/AppKit/NSViewController.swift +++ b/Source/AppKit/NSViewController.swift @@ -8,15 +8,17 @@ import Foundation import ReactiveCocoa +import Result extension NSViewController { /// Provides a "trigger signal" to allow us easier access to viewWillDisappear calls + @available(OSX 10.10, *) public var rex_viewWillDisappear: Signal { return associatedObject(self, key: &viewWillDisappearKey) { var returnedSignal: Signal! - $0.rac_signalForSelector(Selector("viewWillDisappear")) + $0.rac_signalForSelector(#selector(NSViewController.viewWillDisappear)) .toSignalProducer() .map { _ in () } .flatMapError { error in diff --git a/Source/Bindings/BindingValueConsumer.swift b/Source/Bindings/BindingValueConsumer.swift index 0c66afe..47dbc63 100644 --- a/Source/Bindings/BindingValueConsumer.swift +++ b/Source/Bindings/BindingValueConsumer.swift @@ -11,8 +11,8 @@ import ReactiveCocoa /// A consumer of `BindingValue`s public protocol BindingValueConsumer { - typealias Value - typealias Error: ErrorType + associatedtype Value + associatedtype Error: ErrorType /// Bind the object to a producer of `BindingValue`s func bindToProducer(producer: SignalProducer, Error>) diff --git a/Source/Bindings/ConsumerBinding.swift b/Source/Bindings/ConsumerBinding.swift index 09abaf5..2194e6f 100644 --- a/Source/Bindings/ConsumerBinding.swift +++ b/Source/Bindings/ConsumerBinding.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result /// The `ConsumerBinding` represents a single point where values can be "bound" to a UI object that consumes the specified `Value` type and executes control-specific behavior upon receipt of those values. /// diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift index 6cadb75..3b92e18 100644 --- a/Source/Bindings/PropertyBinding.swift +++ b/Source/Bindings/PropertyBinding.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result /// A `PropertyBinding` matches up a property with a function that validates incoming values to the property, and wraps the two into a new `MutablePropertyType` that can be treated as its own property, and bound to. public final class PropertyBinding { diff --git a/Source/Bindings/TypedDynamicProperty.swift b/Source/Bindings/TypedDynamicProperty.swift index 93ee180..415e2e6 100644 --- a/Source/Bindings/TypedDynamicProperty.swift +++ b/Source/Bindings/TypedDynamicProperty.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result /// Wraps a `DynamicProperty` instance in an effort to handle the casting that is often required when bridging from the KVO-powered class. public final class TypedDynamicProperty : MutablePropertyType { diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 657a039..34800e4 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result extension NSObject { /// Creates a strongly-typed producer to monitor `keyPath` via KVO. The caller diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/NSUserDefaults.swift index 65dd33a..d8de84c 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/NSUserDefaults.swift @@ -8,6 +8,7 @@ import Foundation import ReactiveCocoa +import Result extension NSUserDefaults { /// Sends value of `key` whenever it changes. Attempts to filter out repeats diff --git a/Source/Property.swift b/Source/Property.swift index 2da5003..8cd4d75 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import Result extension PropertyType where Value == Bool { /// The conjunction of `self` and `other`. diff --git a/Source/Signal.swift b/Source/Signal.swift index c587bb4..d55b5b0 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import Result extension SignalType { diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index b0d8f2a..839eecc 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -7,6 +7,7 @@ // import ReactiveCocoa +import Result extension SignalProducerType { diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index f92da0a..0242ca2 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -8,6 +8,7 @@ import ReactiveCocoa import UIKit +import Result extension UIControl { #if os(iOS) diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index efe00db..df5f08c 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -9,6 +9,7 @@ import Foundation import ReactiveCocoa import UIKit +import Result extension UITextField { From 2df4b4b9d0fc871dcf01b14ea52bd40b982f1cbb Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 10 Jul 2016 14:05:39 +0800 Subject: [PATCH 08/31] Swift 3 enablement. --- Cartfile | 2 +- Cartfile.resolved | 4 +- Rex.xcodeproj/project.pbxproj | 111 ++++++----- .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 2 +- .../xcshareddata/xcschemes/Rex-iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/Rex-tvOS.xcscheme | 2 +- .../xcschemes/Rex-watchOS.xcscheme | 2 +- Source/Action.swift | 4 +- Source/AppKit/NSTextField.swift | 4 +- Source/Foundation/Association.swift | 21 +-- Source/Foundation/Data.swift | 39 ++++ Source/Foundation/NSObject.swift | 7 +- ...SUserDefaults.swift => UserDefaults.swift} | 13 +- Source/Property.swift | 42 ++--- Source/RACSignal.swift | 2 - Source/Signal.swift | 53 +++--- Source/SignalProducer.swift | 172 +++++++++++++++--- Source/UIKit/UIBarButtonItem.swift | 2 +- Source/UIKit/UIBarItem.swift | 2 +- Source/UIKit/UIButton.swift | 10 +- Source/UIKit/UIControl.swift | 16 +- Source/UIKit/UIDatePicker.swift | 2 +- Source/UIKit/UILabel.swift | 2 +- Source/UIKit/UISegmentedControl.swift | 2 +- Source/UIKit/UISwitch.swift | 2 +- Source/UIKit/UITextField.swift | 2 +- Source/UIKit/UITextView.swift | 4 +- Source/UIKit/UIView.swift | 6 +- Source/UIKit/UIViewController.swift | 14 +- 29 files changed, 344 insertions(+), 202 deletions(-) create mode 100644 Source/Foundation/Data.swift rename Source/Foundation/{NSUserDefaults.swift => UserDefaults.swift} (69%) diff --git a/Cartfile b/Cartfile index fb56d4c..87745f3 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.1 +github "ReactiveCocoa/ReactiveCocoa" "RAC5-swift3" diff --git a/Cartfile.resolved b/Cartfile.resolved index 421a6de..a3bf652 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "2.1.1" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.1" +github "antitypical/Result" "2fe88d1d41615ed060489e8cd06fc1b3e8f0ca53" +github "ReactiveCocoa/ReactiveCocoa" "3073a487acd53008a41b89ef6512040a1347212d" diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index a043fc4..ae90df0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -56,8 +56,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -71,8 +71,8 @@ D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; - D834573A1AFEE4BE0070616A /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D834573D1AFEE57E0070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; D83457411AFEE6050070616A /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -90,9 +90,9 @@ D8715DA11C211011005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8715DA21C211014005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; D8715DA31C21107F005F4191 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D8715DA41C21107F005F4191 /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8715DA41C21107F005F4191 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8715DA51C21107F005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; + D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */; }; D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DA81C2110DA005F4191 /* Result.framework */; }; D8715DB91C2112A9005F4191 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -101,9 +101,9 @@ D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8715DBD1C2112D1005F4191 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8715DBE1C2112D6005F4191 /* Association.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86FFBD01B34AD6F001A89B3 /* Association.swift */; }; - D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8715DBF1C2112D6005F4191 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; - D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; + D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DC51C211310005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8715DC61C211553005F4191 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; @@ -125,8 +125,8 @@ D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8289A2E61BD7F7730097FB60 /* UIViewTests.swift */; }; D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; D8715DE71C21170C005F4191 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; - D8715DEA1C211739005F4191 /* Result.framework in (null) */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC21C211310005F4191 /* ReactiveCocoa.framework */; }; + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8715DC31C211310005F4191 /* Result.framework */; }; D8A454061BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454071BD26A1A00C9E790 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454051BD26A1A00C9E790 /* Property.swift */; }; D8A454091BD2772700C9E790 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A454081BD2772700C9E790 /* PropertyTests.swift */; }; @@ -134,18 +134,18 @@ D8E4A6201B7BBB1600EAD8A8 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */; }; D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5173EBC51B625A2600C9B48E /* UIBarItem.swift */; }; D8F073161B863CE70047D546 /* UILabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F073141B861B3A0047D546 /* UILabelTests.swift */; }; - D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; - D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; - D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */; }; - D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* NSData.swift */; }; + D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; + D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; + D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973C1B17F30D002E15BA /* UserDefaults.swift */; }; + D8F0973F1B17F31E002E15BA /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F0973A1B17F2F7002E15BA /* Data.swift */; }; D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; D8F097451B17F3C8002E15BA /* NSObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097431B17F3C8002E15BA /* NSObject.swift */; }; + D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; + D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; E6933BEA1CD9C335006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */; }; E6933BEC1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */; }; - D8F0974A1B17F5E1002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; - D8F0974B1B17F5E2002E15BA /* NSObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F097471B17F5DD002E15BA /* NSObjectTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -173,49 +173,50 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* (null) */ = { + D8003EB21AFEC6A800D7D3C5 /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in (null) */, - D8003EB51AFEC6B000D7D3C5 /* Result.framework in (null) */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */, ); + name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; - D83457371AFEE4B80070616A /* (null) */ = { + D83457371AFEE4B80070616A /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in (null) */, - D834573A1AFEE4BE0070616A /* Result.framework in (null) */, + D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; - D8715DE81C21172A005F4191 /* (null) */ = { + D8715DE81C21172A005F4191 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8715DE91C211739005F4191 /* ReactiveCocoa.framework in (null) */, - D8715DEA1C211739005F4191 /* Result.framework in (null) */, + D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, + D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 45CED46B1D27BB8700788BDC /* UIActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorView.swift; sourceTree = ""; }; 45CED46D1D27C1D400788BDC /* UIActivityIndicatorViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorViewTests.swift; sourceTree = ""; }; - 4238D5951B4D5950008534C0 /* NSTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = NSTextField.swift; path = AppKit/NSTextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; - 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItem.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5B1C882D1D0715CE000B888F /* UISegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = ""; }; + 5B1C882F1D071639000B888F /* UISegmentedControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISegmentedControlTests.swift; sourceTree = ""; }; 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIControl+EnableSendActionsForControlEvents.swift"; sourceTree = ""; }; 7D2AA99A1CB6EFEB008AB5C9 /* UISwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitch.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7D2AA99C1CB6F275008AB5C9 /* UISwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UISwitchTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -273,12 +274,12 @@ D8A454051BD26A1A00C9E790 /* Property.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Property.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8A454081BD2772700C9E790 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PropertyTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F073141B861B3A0047D546 /* UILabelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UILabelTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - D8F0973A1B17F2F7002E15BA /* NSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSData.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; - E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; - D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSUserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8F0973A1B17F2F7002E15BA /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Data.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + D8F0973C1B17F30D002E15BA /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097431B17F3C8002E15BA /* NSObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D8F097471B17F5DD002E15BA /* NSObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = NSObjectTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E6933BE61CD9C0B2006F7CE7 /* UIProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressView.swift; sourceTree = ""; }; + E6933BE81CD9C1F1006F7CE7 /* UIProgressViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -371,7 +372,10 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */, D8003EAA1AFEC57200D7D3C5 /* Binaries */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 0; }; D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; @@ -530,9 +534,9 @@ isa = PBXGroup; children = ( D86FFBD01B34AD6F001A89B3 /* Association.swift */, - D8F0973A1B17F2F7002E15BA /* NSData.swift */, + D8F0973A1B17F2F7002E15BA /* Data.swift */, D8F097431B17F3C8002E15BA /* NSObject.swift */, - D8F0973C1B17F30D002E15BA /* NSUserDefaults.swift */, + D8F0973C1B17F30D002E15BA /* UserDefaults.swift */, ); path = Foundation; sourceTree = ""; @@ -608,7 +612,7 @@ D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* (null) */, + D8003EB21AFEC6A800D7D3C5 /* Copy Files */, ); buildRules = ( ); @@ -645,7 +649,7 @@ D834571A1AFEE44E0070616A /* Sources */, D834571B1AFEE44E0070616A /* Frameworks */, D834571C1AFEE44E0070616A /* Resources */, - D83457371AFEE4B80070616A /* (null) */, + D83457371AFEE4B80070616A /* CopyFiles */, ); buildRules = ( ); @@ -700,7 +704,7 @@ D8715DCD1C21160A005F4191 /* Sources */, D8715DCE1C21160A005F4191 /* Frameworks */, D8715DCF1C21160A005F4191 /* Resources */, - D8715DE81C21172A005F4191 /* (null) */, + D8715DE81C21172A005F4191 /* CopyFiles */, ); buildRules = ( ); @@ -719,7 +723,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { D8003E8D1AFEC3D400D7D3C5 = { @@ -745,7 +749,7 @@ }; }; }; - buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */; + buildConfigurationList = D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -832,9 +836,9 @@ D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, D8F097441B17F3C8002E15BA /* NSObject.swift in Sources */, - D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, - D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -864,7 +868,7 @@ 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, - D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, @@ -879,7 +883,7 @@ C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, - D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, + D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, @@ -925,9 +929,9 @@ D8715DA51C21107F005F4191 /* NSObject.swift in Sources */, D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */, C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */, - D8715DA41C21107F005F4191 /* NSData.swift in Sources */, + D8715DA41C21107F005F4191 /* Data.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, - D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, + D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -951,12 +955,12 @@ D8715DBC1C2112D1005F4191 /* Signal.swift in Sources */, C72CF3E71CBF188A00E19897 /* RACSignal.swift in Sources */, E6933BED1CD9C37D006F7CE7 /* UIProgressView.swift in Sources */, - D8715DBF1C2112D6005F4191 /* NSData.swift in Sources */, + D8715DBF1C2112D6005F4191 /* Data.swift in Sources */, D8715DCC1C211553005F4191 /* UIView.swift in Sources */, D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, D8715DC81C211553005F4191 /* UIButton.swift in Sources */, D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, - D8715DC11C2112D6005F4191 /* NSUserDefaults.swift in Sources */, + D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */, 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1043,6 +1047,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1083,6 +1088,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; @@ -1109,7 +1115,6 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; @@ -1135,10 +1140,10 @@ INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -1175,6 +1180,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -1231,6 +1237,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1274,6 +1281,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1321,6 +1329,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1370,6 +1379,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -1404,6 +1414,7 @@ PRODUCT_BUNDLE_IDENTIFIER = me.neilpa.RexTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; }; @@ -1412,7 +1423,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex-Mac" */ = { + D8003E881AFEC3D400D7D3C5 /* Build configuration list for PBXProject "Rex" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA21AFEC3D400D7D3C5 /* Debug */, diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index cc58cdc..3a84fae 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -1,6 +1,6 @@ { - return self.executing.signal + return self.isExecuting.signal .filterMap { $0 ? () : nil } } @@ -25,7 +25,7 @@ extension Action { public var rex_completed: Signal { return events .filterMap { event -> Void? in - if case .Completed = event { + if case .completed = event { return () } else { return nil diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index cf14eee..4f2ba98 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -13,8 +13,8 @@ import enum Result.NoError extension NSTextField { /// Sends the field's string value whenever it changes. public var rex_textSignal: SignalProducer { - return NSNotificationCenter.defaultCenter() - .rac_notifications(NSControlTextDidChangeNotification, object: self) + return NotificationCenter.default + .rac_notifications(forName: .NSControlTextDidChange, object: self) .map { notification in (notification.object as! NSTextField).stringValue } diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 976b6c9..8be4d03 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,13 +15,12 @@ import ReactiveCocoa /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { - let initial: AnyObject -> String = { host in - host.valueForKeyPath(keyPath.stringValue) as? String ?? "" +public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { + let initial: (AnyObject) -> String = { host in + host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -33,13 +32,12 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: AnyObject, keyPath: StaticString, @noescape placeholder: () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: { host in - host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -49,8 +47,7 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -@warn_unused_result(message="Did you forget to use the property?") -public func associatedProperty(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T, setter: (Host, T) -> (), @noescape setUp: MutableProperty -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -69,7 +66,7 @@ public func associatedProperty(host: Host, key: UnsafePointe /// 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`. -public func associatedObject(host: Host, key: UnsafePointer<()>, @noescape initial: Host -> T) -> T { +public func associatedObject(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift new file mode 100644 index 0000000..91d5403 --- /dev/null +++ b/Source/Foundation/Data.swift @@ -0,0 +1,39 @@ +// +// Data.swift +// Rex +// +// Created by Ilya Laryionau on 10/05/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveCocoa + +extension Data { + /// Read the data at the URL, sending the result or an error. + public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { + return SignalProducer { observer, disposable in + do { + let data = try Data(contentsOf: url, options: options) + observer.sendNext(data) + observer.sendCompleted() + } catch { + observer.sendFailed(error as NSError) + } + } + } +} + +extension NSData { + /// Read the data at the URL, sending the result or an error. + public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { + return SignalProducer { observer, disposable in + do { + let data = try NSData(contentsOf: url, options: options) + observer.sendNext(data) + observer.sendCompleted() + } catch { + observer.sendFailed(error as NSError) + } + } + } +} diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index c839ef1..5747ef1 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -15,16 +15,15 @@ extension NSObject { /// /// Swift classes deriving `NSObject` must declare properties as `dynamic` for /// them to work with KVO. However, this is not recommended practice. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func rex_producerForKeyPath(keyPath: String) -> SignalProducer { - return self.rac_valuesForKeyPath(keyPath, observer: nil) + public func rex_producerForKeyPath(_ keyPath: String) -> SignalProducer { + return self.rac_values(forKeyPath: keyPath, observer: nil) .toSignalProducer() .map { $0 as! T } .flatMapError { error in // Errors aren't possible, but the compiler doesn't know that. assertionFailure("Unexpected error from KVO signal: \(error)") return .empty - } + } } /// Creates a signal that will be triggered when the object diff --git a/Source/Foundation/NSUserDefaults.swift b/Source/Foundation/UserDefaults.swift similarity index 69% rename from Source/Foundation/NSUserDefaults.swift rename to Source/Foundation/UserDefaults.swift index 702dd93..73282cd 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -9,21 +9,20 @@ import ReactiveCocoa import enum Result.NoError -extension NSUserDefaults { +extension UserDefaults { /// Sends value of `key` whenever it changes. Attempts to filter out repeats /// by casting to NSObject and checking for equality. If the values aren't /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func rex_valueForKey(key: String) -> SignalProducer { - let center = NSNotificationCenter.defaultCenter() - let initial = objectForKey(key) + public func rex_valueForKey(_ key: String) -> SignalProducer { + let center = NotificationCenter.default + let initial = object(forKey: key) - let changes = center.rac_notifications(NSUserDefaultsDidChangeNotification) + let changes = center.rac_notifications(forName: UserDefaults.didChangeNotification) .map { _ in // The notification doesn't provide what changed so we have to look // it up every time - self.objectForKey(key) + self.object(forKey: key) } return SignalProducer(value: initial) diff --git a/Source/Property.swift b/Source/Property.swift index 230d0ab..c743c4e 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -9,40 +9,35 @@ import ReactiveCocoa import enum Result.NoError -extension PropertyType where Value == Bool { +extension PropertyProtocol where Value == Bool { /// The conjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func and(other: P) -> AndProperty { + public func and(_ other: P) -> AndProperty { return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) } /// The conjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func and(other: AnyProperty) -> AndProperty { + public func and(_ other: AnyProperty) -> AndProperty { return AndProperty(terms: [AnyProperty(self), other]) } /// The disjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: P) -> OrProperty { + public func or(_ other: P) -> OrProperty { return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) } /// The disjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: AnyProperty) -> OrProperty { + public func or(_ other: AnyProperty) -> OrProperty { return OrProperty(terms: [AnyProperty(self), other]) } /// A negated property of `self`. - @warn_unused_result(message="Did you forget to use the property?") public func not() -> NotProperty { return NotProperty(source: AnyProperty(self), invert: true) } } /// Specialized `PropertyType` for the conjuction of a set of boolean properties. -public struct AndProperty: PropertyType { +public struct AndProperty: PropertyProtocol { public let terms: [AnyProperty] public var value: Bool { @@ -51,27 +46,25 @@ public struct AndProperty: PropertyType { public var producer: SignalProducer { let producers = terms.map { $0.producer } - return combineLatest(producers).map { values in + return SignalProducer.combineLatest(producers).map { values in return values.reduce(true) { $0 && $1 } } } public var signal: Signal { let signals = terms.map { $0.signal } - return combineLatest(signals).map { values in + return Signal.combineLatest(signals).map { values in return values.reduce(true) { $0 && $1 } } } /// Creates a new property with an additional conjunctive term. - @warn_unused_result(message="Did you forget to use the property?") - public func and

(other: P) -> AndProperty { + public func and

(_ other: P) -> AndProperty { return AndProperty(terms: terms + [AnyProperty(other)]) } /// Creates a new property with an additional conjunctive term. - @warn_unused_result(message="Did you forget to use the property?") - public func and(other: AnyProperty) -> AndProperty { + public func and(_ other: AnyProperty) -> AndProperty { return AndProperty(terms: terms + [other]) } @@ -81,7 +74,7 @@ public struct AndProperty: PropertyType { } /// Specialized `PropertyType` for the disjunction of a set of boolean properties. -public struct OrProperty: PropertyType { +public struct OrProperty: PropertyProtocol { public let terms: [AnyProperty] public var value: Bool { @@ -90,27 +83,25 @@ public struct OrProperty: PropertyType { public var producer: SignalProducer { let producers = terms.map { $0.producer } - return combineLatest(producers).map { values in + return SignalProducer.combineLatest(producers).map { values in return values.reduce(false) { $0 || $1 } } } public var signal: Signal { let signals = terms.map { $0.signal } - return combineLatest(signals).map { values in + return Signal.combineLatest(signals).map { values in return values.reduce(false) { $0 || $1 } } } /// Creates a new property with an additional disjunctive term. - @warn_unused_result(message="Did you forget to use the property?") - public func or

(other: P) -> OrProperty { + public func or

(_ other: P) -> OrProperty { return OrProperty(terms: terms + [AnyProperty(other)]) } /// Creates a new property with an additional disjunctive term. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: AnyProperty) -> OrProperty { + public func or(_ other: AnyProperty) -> OrProperty { return OrProperty(terms: terms + [other]) } @@ -120,7 +111,7 @@ public struct OrProperty: PropertyType { } /// Specialized `PropertyType` for the negation of a boolean property. -public struct NotProperty: PropertyType { +public struct NotProperty: PropertyProtocol { private let source: AnyProperty private let invert: Bool @@ -137,7 +128,6 @@ public struct NotProperty: PropertyType { } /// A negated property of `self`. - @warn_unused_result(message="Did you forget to use the property?") public func not() -> NotProperty { return NotProperty(source: source, invert: !invert) } diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 8d01fa5..f961cc5 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -17,7 +17,6 @@ extension RACSignal { /// for certain things, like event streams (see `UIControl.signalForControlEvents`) /// use this method to be able to expose these inherently hot streams /// as `Signal`s. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") public func rex_toSignal() -> Signal { return Signal { observer in return self.toSignalProducer().start(observer) @@ -27,7 +26,6 @@ extension RACSignal { /// Converts `self` into a `Signal`, that can be used /// with the `takeUntil` operator, or as an "activation" signal. /// (e.g. a button) - @warn_unused_result(message="Did you forget to call `observe` on the signal?") public final func rex_toTriggerSignal() -> Signal<(), NoError> { return self .rex_toSignal() diff --git a/Source/Signal.swift b/Source/Signal.swift index 4bc4837..d953075 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -9,24 +9,23 @@ import ReactiveCocoa import enum Result.NoError -extension SignalType { +extension SignalProtocol { /// Applies `transform` to values from `signal` with non-`nil` results unwrapped and /// forwared on the returned signal. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func filterMap(transform: Value -> U?) -> Signal { + public func filterMap(_ transform: (Value) -> U?) -> Signal { return Signal { observer in return self.observe { event in switch event { - case let .Next(value): + case let .next(value): if let mapped = transform(value) { observer.sendNext(mapped) } - case let .Failed(error): + case let .failed(error): observer.sendFailed(error) - case .Completed: + case .completed: observer.sendCompleted() - case .Interrupted: + case .interrupted: observer.sendInterrupted() } } @@ -35,20 +34,19 @@ extension SignalType { /// Returns a signal that drops `Error` sending `replacement` terminal event /// instead, defaulting to `Completed`. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func ignoreError(replacement replacement: Event = .Completed) -> Signal { + public func ignoreError(replacement: Event = .completed) -> Signal { precondition(replacement.isTerminating) return Signal { observer in return self.observe { event in switch event { - case let .Next(value): + case let .next(value): observer.sendNext(value) - case .Failed: + case .failed: observer.action(replacement) - case .Completed: + case .completed: observer.sendCompleted() - case .Interrupted: + case .interrupted: observer.sendInterrupted() } } @@ -60,16 +58,15 @@ extension SignalType { /// /// If the interval is 0, the timeout will be scheduled immediately. The signal /// must complete synchronously (or on a faster scheduler) to avoid the timeout. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> Signal { + public func timeout(after interval: TimeInterval, with event: Event, on scheduler: DateSchedulerProtocol) -> Signal { precondition(interval >= 0) precondition(event.isTerminating) return Signal { observer in let disposable = CompositeDisposable() - let date = scheduler.currentDate.dateByAddingTimeInterval(interval) - disposable += scheduler.scheduleAfter(date) { + let date = scheduler.currentDate.addingTimeInterval(interval) + disposable += scheduler.schedule(after: date) { observer.action(event) } @@ -85,16 +82,15 @@ extension SignalType { /// /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func muteFor(interval: NSTimeInterval, clock: DateSchedulerType) -> Signal { + public func muteFor(_ interval: TimeInterval, clock: DateSchedulerProtocol) -> Signal { precondition(interval > 0) var expires = clock.currentDate return filter { _ in let now = clock.currentDate - if expires.compare(now) != .OrderedDescending { - expires = now.dateByAddingTimeInterval(interval) + if expires.compare(now) != .orderedDescending { + expires = now.addingTimeInterval(interval) return true } return false @@ -102,20 +98,19 @@ extension SignalType { } } -extension SignalType where Value: SequenceType { +extension SignalProtocol where Value: Sequence { /// Returns a signal that flattens sequences of elements. The inverse of `collect`. - @warn_unused_result(message="Did you forget to call `observe` on the signal?") - public func uncollect() -> Signal { - return Signal { observer in + public func uncollect() -> Signal { + return Signal { observer in return self.observe { event in switch event { - case let .Next(sequence): + case let .next(sequence): sequence.forEach { observer.sendNext($0) } - case let .Failed(error): + case let .failed(error): observer.sendFailed(error) - case .Completed: + case .completed: observer.sendCompleted() - case .Interrupted: + case .interrupted: observer.sendInterrupted() } } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index c977428..e897c47 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -9,28 +9,27 @@ import ReactiveCocoa import enum Result.NoError -extension SignalProducerType { +extension SignalProducerProtocol { /// Buckets each received value into a group based on the key returned /// from `grouping`. Termination events on the original signal are /// also forwarded to each producer group. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func groupBy(grouping: Value -> Key) -> SignalProducer<(Key, SignalProducer), Error> { + public func group(by grouping: (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in var groups: [Key: Signal.Observer] = [:] - let lock = NSRecursiveLock() + let lock = RecursiveLock() lock.name = "me.neilpa.rex.groupBy" self.start { event in switch event { - case let .Next(value): + case let .next(value): let key = grouping(value) lock.lock() var group = groups[key] if group == nil { - let (producer, innerObserver) = SignalProducer.buffer(Int.max) + let (producer, innerObserver) = SignalProducer.bufferingProducer(upTo: Int.max) observer.sendNext(key, producer) groups[key] = innerObserver @@ -40,15 +39,15 @@ extension SignalProducerType { group!.sendNext(value) - case let .Failed(error): + case let .failed(error): observer.sendFailed(error) groups.values.forEach { $0.sendFailed(error) } - case .Completed: + case .completed: observer.sendCompleted() groups.values.forEach { $0.sendCompleted() } - case .Interrupted: + case .interrupted: observer.sendInterrupted() groups.values.forEach { $0.sendInterrupted() } } @@ -58,15 +57,13 @@ extension SignalProducerType { /// Applies `transform` to values from self with non-`nil` results unwrapped and /// forwared on the returned producer. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func filterMap(transform: Value -> U?) -> SignalProducer { + public func filterMap(_ transform: (Value) -> U?) -> SignalProducer { return lift { $0.filterMap(transform) } } /// Returns a producer that drops `Error` sending `replacement` terminal event /// instead, defaulting to `Completed`. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func ignoreError(replacement replacement: Event = .Completed) -> SignalProducer { + public func ignoreError(replacement: Event = .completed) -> SignalProducer { precondition(replacement.isTerminating) return lift { $0.ignoreError(replacement: replacement) } } @@ -76,9 +73,8 @@ extension SignalProducerType { /// /// If the interval is 0, the timeout will be scheduled immediately. The producer /// must complete synchronously (or on a faster scheduler) to avoid the timeout. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func timeoutAfter(interval: NSTimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerType) -> SignalProducer { - return lift { $0.timeoutAfter(interval, withEvent: event, onScheduler: scheduler) } + public func timeout(after interval: TimeInterval, with event: Event, on scheduler: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.timeout(after: interval, with: event, on: scheduler) } } /// Forwards a value and then mutes the producer by dropping all subsequent values @@ -88,22 +84,19 @@ extension SignalProducerType { /// /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func muteFor(interval: NSTimeInterval, clock: DateSchedulerType) -> SignalProducer { + public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> SignalProducer { return lift { $0.muteFor(interval, clock: clock) } } /// Delays the start of the producer by `interval` on the provided scheduler. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func deferred(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType) -> SignalProducer { + public func `defer`(by interval: TimeInterval, on scheduler: DateSchedulerProtocol) -> SignalProducer { return SignalProducer.empty - .delay(interval, onScheduler: scheduler) + .delay(interval, on: scheduler) .concat(self.producer) } /// Delays retrying on failure by `interval` up to `count` attempts. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func deferredRetry(interval: NSTimeInterval, onScheduler scheduler: DateSchedulerType, count: Int = .max) -> SignalProducer { + public func deferredRetry(interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { precondition(count >= 0) if count == 0 { @@ -115,20 +108,143 @@ extension SignalProducerType { // The final attempt shouldn't defer the error if it fails var producer = SignalProducer(error: error) if retries > 0 { - producer = producer.deferred(interval, onScheduler: scheduler) + producer = producer.defer(by: interval, on: scheduler) } retries -= 1 return producer } - .retry(count) + .retry(upTo: count) } } -extension SignalProducerType where Value: SequenceType { +extension SignalProducerProtocol where Value: Sequence { /// Returns a producer that flattens sequences of elements. The inverse of `collect`. - @warn_unused_result(message="Did you forget to call `start` on the producer?") - public func uncollect() -> SignalProducer { + public func uncollect() -> SignalProducer { return lift { $0.uncollect() } } } + +/// Temporary replacement of `buffer(upTo:)`. +/// https://github.com/ReactiveCocoa/ReactiveCocoa/blob/RAC5-swift3/ReactiveCocoa/Swift/SignalProducer.swift + +extension SignalProducer { + private static func bufferingProducer(upTo capacity: Int) -> (SignalProducer, Signal.Observer) { + precondition(capacity >= 0, "Invalid capacity: \(capacity)") + + // Used as an atomic variable so we can remove observers without needing + // to run on a serial queue. + let state: Atomic> = Atomic(BufferState()) + + let producer = self.init { observer, disposable in + // Assigned to when replay() is invoked synchronously below. + var token: RemovalToken? + + let replayBuffer = ReplayBuffer() + var replayValues: [Value] = [] + var replayToken: RemovalToken? + var next = state.modify { state in + replayValues = state.values + if replayValues.isEmpty { + token = state.observers?.insert(observer) + } else { + replayToken = state.replayBuffers.insert(replayBuffer) + } + } + + while !replayValues.isEmpty { + replayValues.forEach(observer.sendNext) + + next = state.modify { state in + replayValues = replayBuffer.values + replayBuffer.values = [] + if replayValues.isEmpty { + if let replayToken = replayToken { + state.replayBuffers.remove(using: replayToken) + } + token = state.observers?.insert(observer) + } + } + } + + if let terminationEvent = next.terminationEvent { + observer.action(terminationEvent) + } + + if let token = token { + disposable += { + state.modify { state in + state.observers?.remove(using: token) + } + } + } + } + + let bufferingObserver: Signal.Observer = Observer { event in + let originalState = state.modify { state in + if let value = event.value { + state.add(value, upTo: capacity) + } else { + // Disconnect all observers and prevent future + // attachments. + state.terminationEvent = event + state.observers = nil + } + } + + originalState.observers?.forEach { $0.action(event) } + } + + return (producer, bufferingObserver) + } +} + +/// A uniquely identifying token for Observers that are replaying values in +/// BufferState. +private final class ReplayBuffer { + private var values: [Value] = [] +} + +private struct BufferState { + /// All values in the buffer. + var values: [Value] = [] + + /// Any terminating event sent to the buffer. + /// + /// This will be nil if termination has not occurred. + var terminationEvent: Event? + + /// The observers currently attached to the buffered producer, or nil if the + /// producer was terminated. + var observers: Bag.Observer>? = Bag() + + /// The set of unused replay token identifiers. + var replayBuffers: Bag> = Bag() + + /// Appends a new value to the buffer, trimming it down to the given capacity + /// if necessary. + mutating func add(_ value: Value, upTo capacity: Int) { + precondition(capacity >= 0) + + for buffer in replayBuffers { + buffer.values.append(value) + } + + if capacity == 0 { + values = [] + return + } + + if capacity == 1 { + values = [ value ] + return + } + + values.append(value) + + let overflow = values.count - capacity + if overflow > 0 { + values.removeSubrange(0.. { - return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 }) + return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) } } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index ea49aef..81bda04 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -20,13 +20,13 @@ extension UIButton { let property = MutableProperty(initial) property.producer - .combinePrevious(initial) + .combinePrevious(initial: initial) .startWithNext { [weak host] previous, next in - host?.removeTarget(previous, action: CocoaAction.selector, forControlEvents: .TouchUpInside) - host?.addTarget(next, action: CocoaAction.selector, forControlEvents: .TouchUpInside) + host?.removeTarget(previous, action: CocoaAction.selector, for: .touchUpInside) + host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) } - host.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } + host.rex_enabled <~ property.producer.flatMap(.latest) { $0.rex_enabledProducer } return property } @@ -35,7 +35,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 { - return associatedProperty(self, key: &titleKey, initial: { $0.titleForState(.Normal) ?? "" }, setter: { $0.setTitle($1, forState: .Normal) }) + return associatedProperty(self, key: &titleKey, initial: { $0.title(for: .normal) ?? "" }, setter: { $0.setTitle($1, for: .normal) }) } } diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 1ad6e9a..49c1d6b 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -14,9 +14,8 @@ extension UIControl { #if os(iOS) /// Creates a producer for the sender whenever a specified control event is triggered. - @warn_unused_result(message="Did you forget to use the property?") - public func rex_controlEvents(events: UIControlEvents) -> SignalProducer { - return rac_signalForControlEvents(events) + public func rex_controlEvents(_ events: UIControlEvents) -> SignalProducer { + return rac_signal(for: events) .toSignalProducer() .map { $0 as? UIControl } .flatMapError { _ in SignalProducer(value: nil) } @@ -27,11 +26,10 @@ extension UIControl { /// This property uses `UIControlEvents.ValueChanged` and `UIControlEvents.EditingChanged` /// events to detect changes and keep the value up-to-date. // - @warn_unused_result(message="Did you forget to use the property?") - class func rex_value(host: Host, getter: Host -> T, setter: (Host, T) -> ()) -> MutableProperty { + class func rex_value(_ host: Host, getter: (Host) -> T, setter: (Host, T) -> ()) -> MutableProperty { return associatedProperty(host, key: &valueChangedKey, initial: getter, setter: setter) { property in property <~ - host.rex_controlEvents([.ValueChanged, .EditingChanged]) + host.rex_controlEvents([.valueChanged, .editingChanged]) .filterMap { $0 as? Host } .filterMap(getter) } @@ -40,17 +38,17 @@ extension UIControl { /// Wraps a control's `enabled` state in a bindable property. public var rex_enabled: MutableProperty { - return associatedProperty(self, key: &enabledKey, initial: { $0.enabled }, setter: { $0.enabled = $1 }) + return associatedProperty(self, key: &enabledKey, initial: { $0.isEnabled }, setter: { $0.isEnabled = $1 }) } /// Wraps a control's `selected` state in a bindable property. public var rex_selected: MutableProperty { - return associatedProperty(self, key: &selectedKey, initial: { $0.selected }, setter: { $0.selected = $1 }) + return associatedProperty(self, key: &selectedKey, initial: { $0.isSelected }, setter: { $0.isSelected = $1 }) } /// Wraps a control's `highlighted` state in a bindable property. public var rex_highlighted: MutableProperty { - return associatedProperty(self, key: &highlightedKey, initial: { $0.highlighted }, setter: { $0.highlighted = $1 }) + return associatedProperty(self, key: &highlightedKey, initial: { $0.isHighlighted }, setter: { $0.isHighlighted = $1 }) } } diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 75f0d38..3fbbd32 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -12,7 +12,7 @@ import UIKit extension UIDatePicker { // Wraps a datePicker's `date` value in a bindable property. - public var rex_date: MutableProperty { + public var rex_date: MutableProperty { return UIControl.rex_value(self, getter: { $0.date }, setter: { $0.date = $1 }) } } diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index c47746f..0078d7c 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -16,7 +16,7 @@ extension UILabel { } /// Wraps a label's `attributedText` value in a bindable property. - public var rex_attributedText: MutableProperty { + public var rex_attributedText: MutableProperty { return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) } diff --git a/Source/UIKit/UISegmentedControl.swift b/Source/UIKit/UISegmentedControl.swift index 465c76e..acd990a 100644 --- a/Source/UIKit/UISegmentedControl.swift +++ b/Source/UIKit/UISegmentedControl.swift @@ -13,7 +13,7 @@ extension UISegmentedControl { /// Wraps a segmentedControls `selectedSegmentIndex` state in a bindable property. public var rex_selectedSegmentIndex: MutableProperty { let property = associatedProperty(self, key: &selectedSegmentIndexKey, initial: { $0.selectedSegmentIndex }, setter: { $0.selectedSegmentIndex = $1 }) - property <~ rex_controlEvents(.ValueChanged) + property <~ rex_controlEvents(.valueChanged) .filterMap { ($0 as? UISegmentedControl)?.selectedSegmentIndex } return property } diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index e02c890..e1b6612 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -13,6 +13,6 @@ extension UISwitch { /// Wraps a switch's `on` value in a bindable property. public var rex_on: MutableProperty { - return UIControl.rex_value(self, getter: { $0.on }, setter: { $0.on = $1 }) + return UIControl.rex_value(self, getter: { $0.isOn }, setter: { $0.isOn = $1 }) } } diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index c9fa38c..5b6d849 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -13,7 +13,7 @@ extension UITextField { /// Wraps a textField's `text` value in a bindable property. public var rex_text: MutableProperty { - let getter: UITextField -> String? = { $0.text } + let getter: (UITextField) -> String? = { $0.text } let setter: (UITextField, String?) -> () = { $0.text = $1 } #if os(iOS) return UIControl.rex_value(self, getter: getter, setter: setter) diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 95a9b19..c78df6d 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -14,8 +14,8 @@ extension UITextView { /// Sends the textView's string value whenever it changes. public var rex_text: SignalProducer { - return NSNotificationCenter.defaultCenter() - .rac_notifications(UITextViewTextDidChangeNotification, object: self) + return NotificationCenter.default + .rac_notifications(forName: .UITextViewTextDidChange, object: self) .filterMap { ($0.object as? UITextView)?.text } } } diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index f0a67f7..2284087 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -17,13 +17,13 @@ extension UIView { /// Wraps a view's `hidden` state in a bindable property. public var rex_hidden: MutableProperty { - return associatedProperty(self, key: &hiddenKey, initial: { $0.hidden }, setter: { $0.hidden = $1 }) + return associatedProperty(self, key: &hiddenKey, initial: { $0.isHidden }, setter: { $0.isHidden = $1 }) } - + /// Wraps a view's `userInteractionEnabled` state in a bindable property. public var rex_userInteractionEnabled: MutableProperty { - return associatedProperty(self, key: &userInteractionEnabledKey, initial: { $0.userInteractionEnabled }, setter: { $0.userInteractionEnabled = $1 }) + return associatedProperty(self, key: &userInteractionEnabledKey, initial: { $0.isUserInteractionEnabled }, setter: { $0.isUserInteractionEnabled = $1 }) } } diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index f16264b..b6ab1ab 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -35,13 +35,13 @@ extension UIViewController { return triggerForSelector(#selector(UIViewController.viewWillAppear(_:))) } - private func triggerForSelector(selector: Selector) -> Signal<(), NoError> { + private func triggerForSelector(_ selector: Selector) -> Signal<(), NoError> { return self - .rac_signalForSelector(selector) + .rac_signal(for: selector) .rex_toTriggerSignal() } - public typealias DismissingCompletion = (Void -> Void)? + public typealias DismissingCompletion = ((Void) -> Void)? public typealias DismissingInformation = (animated: Bool, completion: DismissingCompletion)? /// Wraps a viewController's `dismissViewControllerAnimated` function in a bindable property. @@ -56,16 +56,16 @@ extension UIViewController { /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` public var rex_dismissAnimated: MutableProperty { - let initial: UIViewController -> DismissingInformation = { _ in nil } + let initial: (UIViewController) -> DismissingInformation = { _ in nil } let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in guard let unwrapped = dismissingInfo else { return } - host.dismissViewControllerAnimated(unwrapped.animated, completion: unwrapped.completion) + host.dismiss(animated: unwrapped.animated, completion: unwrapped.completion) } let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in - property <~ self.rac_signalForSelector(#selector(UIViewController.dismissViewControllerAnimated(_:completion:))) - .takeUntilBlock { _ in property.value != nil } + property <~ self.rac_signal(for: #selector(UIViewController.dismiss)) + .take { _ in property.value != nil } .rex_toTriggerSignal() .map { _ in return nil } } From 46f9e0d754871a0c5315e4169727c742f8fd1b0c Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 10 Jul 2016 14:06:06 +0800 Subject: [PATCH 09/31] Update the test cases. --- Tests/ActionTests.swift | 14 +++-- Tests/Foundation/NSObjectTests.swift | 8 +-- ...ol+EnableSendActionsForControlEvents.swift | 56 +++++++++---------- Tests/PropertyTests.swift | 4 +- Tests/SignalProducerTests.swift | 28 +++++----- Tests/SignalTests.swift | 22 ++++---- .../UIKit/UIActivityIndicatorViewTests.swift | 2 +- Tests/UIKit/UIBarButtonItemTests.swift | 6 +- Tests/UIKit/UIButtonTests.swift | 42 +++++++------- .../UIKit/UICollectionReusableViewTests.swift | 8 +-- Tests/UIKit/UIControlTests.swift | 54 +++++++++--------- Tests/UIKit/UIDatePickerTests.swift | 18 +++--- Tests/UIKit/UIImageViewTests.swift | 8 +-- Tests/UIKit/UILabelTests.swift | 26 ++++----- Tests/UIKit/UIProgressViewTests.swift | 4 +- Tests/UIKit/UISwitchTests.swift | 12 ++-- Tests/UIKit/UITableViewCellTests.swift | 3 +- .../UITableViewHeaderFooterViewTests.swift | 8 +-- Tests/UIKit/UITextFieldTests.swift | 8 +-- Tests/UIKit/UITextViewTests.swift | 8 +-- Tests/UIKit/UIViewControllerTests.swift | 26 ++++----- Tests/UIKit/UIViewTests.swift | 24 ++++---- 22 files changed, 196 insertions(+), 193 deletions(-) diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index abd4d1c..b6d7338 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -13,8 +13,8 @@ import enum Result.NoError final class ActionTests: XCTestCase { - enum TestError: ErrorType { - case Unknown + enum TestError: ErrorProtocol { + case unknown } func testStarted() { @@ -33,7 +33,9 @@ final class ActionTests: XCTestCase { } func testCompleted() { - let (producer, observer) = SignalProducer.buffer(Int.max) + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal: signal) + let action = Action { producer } var completed = false @@ -53,7 +55,9 @@ final class ActionTests: XCTestCase { } func testCompletedOnFailed() { - let (producer, observer) = SignalProducer.buffer(Int.max) + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal: signal) + let action = Action { producer } var completed = false @@ -65,7 +69,7 @@ final class ActionTests: XCTestCase { .apply() .start() - observer.sendFailed(.Unknown) + observer.sendFailed(.unknown) XCTAssertFalse(completed) } } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 5ffc070..61e793e 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -25,13 +25,13 @@ final class NSObjectTests: XCTestCase { func testObjectsWillBeDeallocatedSignal() { - let expectation = self.expectationWithDescription("Expected timer to send `completed` event when object deallocates") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected timer to send `completed` event when object deallocates") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let object = Object() - timer(1, onScheduler: QueueScheduler(name: "test.queue")) - .takeUntil(object.rex_willDealloc) + timer(interval: 1, on: QueueScheduler(name: "test.queue")) + .take(until: object.rex_willDealloc) .startWithCompleted { expectation.fulfill() } diff --git a/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift b/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift index 24d5db1..9bd716b 100644 --- a/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift +++ b/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift @@ -8,6 +8,30 @@ import UIKit +private let rex_swizzleToken: Void = { + let originalSelector = #selector(UIControl.sendAction(_:to:for:)) + let swizzledSelector = #selector(UIControl.rex_sendAction(_:to:forEvent:)) + + let originalMethod = class_getInstanceMethod(UIControl.self, originalSelector) + let swizzledMethod = class_getInstanceMethod(UIControl.self, swizzledSelector) + + let didAddMethod = class_addMethod(UIControl.self, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)) + + if didAddMethod { + class_replaceMethod(UIControl.self, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)) + } else { + method_exchangeImplementations(originalMethod, swizzledMethod) + } + + return () +}() + /// Unfortunately, there's an apparent limitation in using `sendActionsForControlEvents` /// on unit-tests for any control besides `UIButton` which is very unfortunate since we /// want test our bindings for `UIDatePicker`, `UISwitch`, `UITextField` and others @@ -16,42 +40,16 @@ import UIKit extension UIControl { public override class func initialize() { - - struct Static { - static var token: dispatch_once_t = 0 - } - if self !== UIControl.self { return } - dispatch_once(&Static.token) { - - let originalSelector = #selector(UIControl.sendAction(_:to:forEvent:)) - let swizzledSelector = #selector(UIControl.rex_sendAction(_:to:forEvent:)) - - let originalMethod = class_getInstanceMethod(self, originalSelector) - let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) - - let didAddMethod = class_addMethod(self, - originalSelector, - method_getImplementation(swizzledMethod), - method_getTypeEncoding(swizzledMethod)) - - if didAddMethod { - class_replaceMethod(self, - swizzledSelector, - method_getImplementation(originalMethod), - method_getTypeEncoding(originalMethod)) - } else { - method_exchangeImplementations(originalMethod, swizzledMethod) - } - } + _ = rex_swizzleToken } // MARK: - Method Swizzling - func rex_sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { - target?.performSelector(action, withObject: self) + func rex_sendAction(_ action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { + _ = target?.perform(action, with: self) } } diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index ea32ccb..6faedac 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -32,7 +32,7 @@ final class PropertyTests: XCTestCase { XCTAssertTrue(current!) let (signal, pipe) = Signal.pipe() - let and2 = and.and(AnyProperty(initialValue: false, signal: signal)) + let and2 = and.and(AnyProperty(initial: false, then: signal)) and2.producer.startWithNext { current = $0 } XCTAssertFalse(and2.value) @@ -62,7 +62,7 @@ final class PropertyTests: XCTestCase { XCTAssertFalse(current!) let (signal, pipe) = Signal.pipe() - let or2 = or.or(AnyProperty(initialValue: true, signal: signal)) + let or2 = or.or(AnyProperty(initial: true, then: signal)) or2.producer.startWithNext { current = $0 } XCTAssertTrue(or2.value) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 35ef801..997ad37 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -14,7 +14,9 @@ import enum Result.NoError final class SignalProducerTests: XCTestCase { func testGroupBy() { - let (producer, observer) = SignalProducer.buffer(Int.max) + let (signal, observer) = Signal.pipe() + let producer = SignalProducer(signal: signal) + var evens: [Int] = [] var odds: [Int] = [] let disposable = CompositeDisposable() @@ -22,7 +24,7 @@ final class SignalProducerTests: XCTestCase { var completed = false disposable += producer - .groupBy { $0 % 2 == 0 } + .group { $0 % 2 == 0 } .start(Observer(next: { key, group in if key { group.startWithNext { evens.append($0) } @@ -62,7 +64,7 @@ final class SignalProducerTests: XCTestCase { var started = false producer - .deferred(1, onScheduler: scheduler) + .defer(by: 1, on: scheduler) .on(started: { started = true }) .start() @@ -72,10 +74,10 @@ final class SignalProducerTests: XCTestCase { scheduler.advance() XCTAssertFalse(deferred) - scheduler.advanceByInterval(0.9) + scheduler.advance(by: 0.9) XCTAssertFalse(deferred) - scheduler.advanceByInterval(0.2) + scheduler.advance(by: 0.2) XCTAssertTrue(deferred) } @@ -86,7 +88,7 @@ final class SignalProducerTests: XCTestCase { let producer = SignalProducer { observer, _ in if count < 2 { scheduler.schedule { observer.sendNext(count) } - scheduler.schedule { observer.sendFailed(.Default) } + scheduler.schedule { observer.sendFailed(.default) } } else { scheduler.schedule { observer.sendCompleted() } } @@ -96,7 +98,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var completed = false producer - .deferredRetry(1, onScheduler: scheduler) + .deferredRetry(interval: 1, on: scheduler) .start(Observer( next: { value = $0 }, completed: { completed = true } @@ -110,12 +112,12 @@ final class SignalProducerTests: XCTestCase { XCTAssertEqual(value, 1) XCTAssertFalse(completed) - scheduler.advanceByInterval(1) + scheduler.advance(by: 1) XCTAssertEqual(count, 2) XCTAssertEqual(value, 2) XCTAssertFalse(completed) - scheduler.advanceByInterval(1) + scheduler.advance(by: 1) XCTAssertEqual(count, 3) XCTAssertEqual(value, 2) XCTAssertTrue(completed) @@ -127,14 +129,14 @@ final class SignalProducerTests: XCTestCase { var count = 0 let producer = SignalProducer { observer, _ in observer.sendNext(count) - observer.sendFailed(.Default) + observer.sendFailed(.default) count += 1 } var value = -1 var failed = false producer - .deferredRetry(1, onScheduler: scheduler, count: 2) + .deferredRetry(interval: 1, on: scheduler, count: 2) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } @@ -148,12 +150,12 @@ final class SignalProducerTests: XCTestCase { XCTAssertEqual(value, 0) XCTAssertFalse(failed) - scheduler.advanceByInterval(1) + scheduler.advance(by: 1) XCTAssertEqual(count, 2) XCTAssertEqual(value, 1) XCTAssertFalse(failed) - scheduler.advanceByInterval(1) + scheduler.advance(by: 1) XCTAssertEqual(count, 3) XCTAssertEqual(value, 2) XCTAssertTrue(failed) diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index af55e1b..d009f32 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -47,7 +47,7 @@ final class SignalTests: XCTestCase { observer.sendNext(1) XCTAssertFalse(completed) - observer.sendFailed(.Default) + observer.sendFailed(.default) XCTAssertTrue(completed) } @@ -56,13 +56,13 @@ final class SignalTests: XCTestCase { var interrupted = false signal - .ignoreError(replacement: .Interrupted) + .ignoreError(replacement: .interrupted) .observeInterrupted { interrupted = true } observer.sendNext(1) XCTAssertFalse(interrupted) - observer.sendFailed(.Default) + observer.sendFailed(.default) XCTAssertTrue(interrupted) } @@ -73,13 +73,13 @@ final class SignalTests: XCTestCase { var completed = false signal - .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + .timeout(after: 2, with: .interrupted, on: scheduler) .observe(Observer( completed: { completed = true }, interrupted: { interrupted = true } )) - scheduler.scheduleAfter(1) { observer.sendCompleted() } + scheduler.schedule(after: 1) { observer.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -96,13 +96,13 @@ final class SignalTests: XCTestCase { var completed = false signal - .timeoutAfter(2, withEvent: .Interrupted, onScheduler: scheduler) + .timeout(after: 2, with: .interrupted, on: scheduler) .observe(Observer( completed: { completed = true }, interrupted: { interrupted = true } )) - scheduler.scheduleAfter(3) { observer.sendCompleted() } + scheduler.schedule(after: 3) { observer.sendCompleted() } XCTAssertFalse(interrupted) XCTAssertFalse(completed) @@ -152,7 +152,7 @@ final class SignalTests: XCTestCase { scheduler.advance() XCTAssertEqual(value, 1) - scheduler.advanceByInterval(1) + scheduler.advance(by: 1) XCTAssertEqual(value, 1) scheduler.schedule { observer.sendNext(5) } @@ -179,13 +179,13 @@ final class SignalTests: XCTestCase { XCTAssertEqual(value, 1) scheduler.schedule { observer.sendNext(2) } - scheduler.schedule { observer.sendFailed(.Default) } + scheduler.schedule { observer.sendFailed(.default) } scheduler.advance() XCTAssertTrue(failed) XCTAssertEqual(value, 1) } } -enum TestError: ErrorType { - case Default +enum TestError: ErrorProtocol { + case `default` } diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index d47d1cb..ece8aef 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -20,7 +20,7 @@ class UIActivityIndicatorTests: XCTestCase { } func testAnimatingProperty() { - let indicatorView = UIActivityIndicatorView(frame: CGRectZero) + let indicatorView = UIActivityIndicatorView(frame: CGRect.zero) _activityIndicatorView = indicatorView let (pipeSignal, observer) = Signal.pipe() diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 8b65c36..d20553b 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -32,15 +32,15 @@ class UIBarButtonItemTests: XCTestCase { func testEnabledProperty() { let barButtonItem = UIBarButtonItem() - barButtonItem.enabled = true + barButtonItem.isEnabled = true let (pipeSignal, observer) = Signal.pipe() barButtonItem.rex_enabled <~ SignalProducer(signal: pipeSignal) observer.sendNext(false) - XCTAssertFalse(barButtonItem.enabled) + XCTAssertFalse(barButtonItem.isEnabled) observer.sendNext(true) - XCTAssertTrue(barButtonItem.enabled) + XCTAssertTrue(barButtonItem.isEnabled) } } diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index b8d33f7..e799e2e 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -13,12 +13,12 @@ import enum Result.NoError extension UIButton { static func button() -> UIButton { - let button = UIButton(type: UIButtonType.Custom) + let button = UIButton(type: UIButtonType.custom) return button; } - override public func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) { - target?.performSelector(action, withObject: nil) + override public func sendAction(_ action: Selector, to target: AnyObject?, for event: UIEvent?) { + target?.perform(action, with: nil) } } @@ -32,15 +32,15 @@ class UIButtonTests: XCTestCase { } func testEnabledPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRectZero) + let button = UIButton(frame: CGRect.zero) _button = button button.rex_enabled <~ SignalProducer(value: false) - XCTAssert(_button?.enabled == false) + XCTAssert(_button?.isEnabled == false) } func testPressedPropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRectZero) + let button = UIButton(frame: CGRect.zero) _button = button let action = Action<(),(),NoError> { @@ -50,37 +50,37 @@ class UIButtonTests: XCTestCase { } func testTitlePropertyDoesntCreateRetainCycle() { - let button = UIButton(frame: CGRectZero) + let button = UIButton(frame: CGRect.zero) _button = button button.rex_title <~ SignalProducer(value: "button") - XCTAssert(_button?.titleForState(.Normal) == "button") + XCTAssert(_button?.title(for: UIControlState()) == "button") } func testTitleProperty() { let firstTitle = "First title" let secondTitle = "Second title" - let button = UIButton(frame: CGRectZero) + let button = UIButton(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() button.rex_title <~ SignalProducer(signal: pipeSignal) - button.setTitle("", forState: .Selected) - button.setTitle("", forState: .Highlighted) + button.setTitle("", for: .selected) + button.setTitle("", for: .highlighted) observer.sendNext(firstTitle) - XCTAssertEqual(button.titleForState(.Normal), firstTitle) - XCTAssertEqual(button.titleForState(.Highlighted), "") - XCTAssertEqual(button.titleForState(.Selected), "") + XCTAssertEqual(button.title(for: UIControlState()), firstTitle) + XCTAssertEqual(button.title(for: .highlighted), "") + XCTAssertEqual(button.title(for: .selected), "") observer.sendNext(secondTitle) - XCTAssertEqual(button.titleForState(.Normal), secondTitle) - XCTAssertEqual(button.titleForState(.Highlighted), "") - XCTAssertEqual(button.titleForState(.Selected), "") + XCTAssertEqual(button.title(for: UIControlState()), secondTitle) + XCTAssertEqual(button.title(for: .highlighted), "") + XCTAssertEqual(button.title(for: .selected), "") } func testPressedProperty() { - let button = UIButton(frame: CGRectZero) - button.enabled = true - button.userInteractionEnabled = true + let button = UIButton(frame: CGRect.zero) + button.isEnabled = true + button.isUserInteractionEnabled = true let passed = MutableProperty(false) let action = Action<(), Bool, NoError> { _ in @@ -90,7 +90,7 @@ class UIButtonTests: XCTestCase { passed <~ SignalProducer(signal: action.values) button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) - button.sendActionsForControlEvents(.TouchUpInside) + button.sendActions(for: .touchUpInside) XCTAssertTrue(passed.value) diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift index b95afac..9c4fea9 100644 --- a/Tests/UIKit/UICollectionReusableViewTests.swift +++ b/Tests/UIKit/UICollectionReusableViewTests.swift @@ -20,16 +20,16 @@ class UICollectionReusableViewTests: XCTestCase { cell.rex_hidden <~ hiddenProperty .producer - .takeUntil(cell.rex_prepareForReuse) + .take(until: cell.rex_prepareForReuse) - XCTAssertFalse(cell.hidden) + XCTAssertFalse(cell.isHidden) hiddenProperty <~ SignalProducer(value: true) - XCTAssertTrue(cell.hidden) + XCTAssertTrue(cell.isHidden) cell.prepareForReuse() hiddenProperty <~ SignalProducer(value: false) - XCTAssertTrue(cell.hidden) + XCTAssertTrue(cell.isHidden) } } diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index b16bfe4..07f35d0 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -21,72 +21,72 @@ class UIControlTests: XCTestCase { } func testEnabledPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRectZero) + let control = UIControl(frame: CGRect.zero) _control = control control.rex_enabled <~ SignalProducer(value: false) - XCTAssert(_control?.enabled == false) + XCTAssert(_control?.isEnabled == false) } func testSelectedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRectZero) + let control = UIControl(frame: CGRect.zero) _control = control control.rex_selected <~ SignalProducer(value: true) - XCTAssert(_control?.selected == true) + XCTAssert(_control?.isSelected == true) } func testHighlightedPropertyDoesntCreateRetainCycle() { - let control = UIControl(frame: CGRectZero) + let control = UIControl(frame: CGRect.zero) _control = control control.rex_highlighted <~ SignalProducer(value: true) - XCTAssert(_control?.highlighted == true) + XCTAssert(_control?.isHighlighted == true) } func testEnabledProperty () { - let control = UIControl(frame: CGRectZero) - control.enabled = false + let control = UIControl(frame: CGRect.zero) + control.isEnabled = false let (pipeSignal, observer) = Signal.pipe() control.rex_enabled <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(control.enabled) + XCTAssertTrue(control.isEnabled) observer.sendNext(false) - XCTAssertFalse(control.enabled) + XCTAssertFalse(control.isEnabled) } func testSelectedProperty() { - let control = UIControl(frame: CGRectZero) - control.selected = false + let control = UIControl(frame: CGRect.zero) + control.isSelected = false let (pipeSignal, observer) = Signal.pipe() control.rex_selected <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(control.selected) + XCTAssertTrue(control.isSelected) observer.sendNext(false) - XCTAssertFalse(control.selected) + XCTAssertFalse(control.isSelected) } func testHighlightedProperty() { - let control = UIControl(frame: CGRectZero) - control.highlighted = false + let control = UIControl(frame: CGRect.zero) + control.isHighlighted = false let (pipeSignal, observer) = Signal.pipe() control.rex_highlighted <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(control.highlighted) + XCTAssertTrue(control.isHighlighted) observer.sendNext(false) - XCTAssertFalse(control.highlighted) + XCTAssertFalse(control.isHighlighted) } func testEnabledAndSelectedProperty() { - let control = UIControl(frame: CGRectZero) - control.selected = false - control.enabled = false + let control = UIControl(frame: CGRect.zero) + control.isSelected = false + control.isEnabled = false let (pipeSignalSelected, observerSelected) = Signal.pipe() let (pipeSignalEnabled, observerEnabled) = Signal.pipe() @@ -95,13 +95,13 @@ class UIControlTests: XCTestCase { observerSelected.sendNext(true) observerEnabled.sendNext(true) - XCTAssertTrue(control.enabled) - XCTAssertTrue(control.selected) + XCTAssertTrue(control.isEnabled) + XCTAssertTrue(control.isSelected) observerSelected.sendNext(false) - XCTAssertTrue(control.enabled) - XCTAssertFalse(control.selected) + XCTAssertTrue(control.isEnabled) + XCTAssertFalse(control.isSelected) observerEnabled.sendNext(false) - XCTAssertFalse(control.enabled) - XCTAssertFalse(control.selected) + XCTAssertFalse(control.isEnabled) + XCTAssertFalse(control.isSelected) } } diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index f29dbd6..20ac37e 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -13,15 +13,15 @@ import Rex class UIDatePickerTests: XCTestCase { - var date: NSDate! + var date: Date! var picker: UIDatePicker! override func setUp() { - let formatter = NSDateFormatter() + let formatter = DateFormatter() formatter.dateFormat = "MM/dd/YYYY" - date = formatter.dateFromString("11/29/1988")! + date = formatter.date(from: "11/29/1988")! - picker = UIDatePicker(frame: CGRectZero) + picker = UIDatePicker(frame: CGRect.zero) } func testUpdatePickerFromProperty() { @@ -31,8 +31,8 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePropertyFromPicker() { - let expectation = self.expectationWithDescription("Expected rex_date to send an event when picker's date value is changed by a UI event") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_date to send an event when picker's date value is changed by a UI event") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } picker.rex_date.signal.observeNext { changedDate in XCTAssertEqual(changedDate, self.date) @@ -40,8 +40,8 @@ class UIDatePickerTests: XCTestCase { } picker.date = date - picker.enabled = true - picker.userInteractionEnabled = true - picker.sendActionsForControlEvents(.ValueChanged) + picker.isEnabled = true + picker.isUserInteractionEnabled = true + picker.sendActions(for: .valueChanged) } } diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index b632f51..b8f4e0f 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -21,7 +21,7 @@ class UIImageViewTests: XCTestCase { } func testImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) _imageView = imageView let image = UIImage() @@ -31,7 +31,7 @@ class UIImageViewTests: XCTestCase { } func testHighlightedImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) _imageView = imageView let image = UIImage() @@ -41,7 +41,7 @@ class UIImageViewTests: XCTestCase { } func testImageProperty() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) let firstChange = UIImage() let secondChange = UIImage() @@ -56,7 +56,7 @@ class UIImageViewTests: XCTestCase { } func testHighlightedImageProperty() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) let firstChange = UIImage() let secondChange = UIImage() diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 9356b9f..ded0e7d 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -21,7 +21,7 @@ class UILabelTests: XCTestCase { } func testTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRectZero) + let label = UILabel(frame: CGRect.zero) _label = label label.rex_text <~ SignalProducer(value: "Test") @@ -32,7 +32,7 @@ class UILabelTests: XCTestCase { let firstChange = "first" let secondChange = "second" - let label = UILabel(frame: CGRectZero) + let label = UILabel(frame: CGRect.zero) label.text = "" let (pipeSignal, observer) = Signal.pipe() @@ -47,21 +47,21 @@ class UILabelTests: XCTestCase { } func testAttributedTextPropertyDoesntCreateRetainCycle() { - let label = UILabel(frame: CGRectZero) + let label = UILabel(frame: CGRect.zero) _label = label - label.rex_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) + label.rex_attributedText <~ SignalProducer(value: AttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } func testAttributedTextProperty() { - let firstChange = NSAttributedString(string: "first") - let secondChange = NSAttributedString(string: "second") + let firstChange = AttributedString(string: "first") + let secondChange = AttributedString(string: "second") - let label = UILabel(frame: CGRectZero) - label.attributedText = NSAttributedString(string: "") + let label = UILabel(frame: CGRect.zero) + label.attributedText = AttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.rex_attributedText <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) @@ -71,13 +71,13 @@ class UILabelTests: XCTestCase { } func testTextColorProperty() { - let firstChange = UIColor.redColor() - let secondChange = UIColor.blackColor() + let firstChange = UIColor.red() + let secondChange = UIColor.black() - let label = UILabel(frame: CGRectZero) + let label = UILabel(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - label.textColor = UIColor.blackColor() + label.textColor = UIColor.black() label.rex_textColor <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) diff --git a/Tests/UIKit/UIProgressViewTests.swift b/Tests/UIKit/UIProgressViewTests.swift index 4fbe695..9e81afd 100644 --- a/Tests/UIKit/UIProgressViewTests.swift +++ b/Tests/UIKit/UIProgressViewTests.swift @@ -20,7 +20,7 @@ class UIProgressViewTests: XCTestCase { } func testProgressPropertyDoesntCreateRetainCycle() { - let progressView = UIProgressView(frame: CGRectZero) + let progressView = UIProgressView(frame: CGRect.zero) _progressView = progressView progressView.rex_progress <~ SignalProducer(value: 0.5) @@ -31,7 +31,7 @@ class UIProgressViewTests: XCTestCase { let firstChange: Float = 0.5 let secondChange: Float = 0.0 - let progressView = UIProgressView(frame: CGRectZero) + let progressView = UIProgressView(frame: CGRect.zero) progressView.progress = 1.0 let (pipeSignal, observer) = Signal.pipe() diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift index 9391052..5098ff7 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -13,19 +13,19 @@ import Result class UISwitchTests: XCTestCase { func testOnProperty() { - let `switch` = UISwitch(frame: CGRectZero) - `switch`.on = false + let `switch` = UISwitch(frame: CGRect.zero) + `switch`.isOn = false let (pipeSignal, observer) = Signal.pipe() `switch`.rex_on <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(`switch`.on) + XCTAssertTrue(`switch`.isOn) observer.sendNext(false) - XCTAssertFalse(`switch`.on) + XCTAssertFalse(`switch`.isOn) - `switch`.on = true - `switch`.sendActionsForControlEvents(.ValueChanged) + `switch`.isOn = true + `switch`.sendActions(for: .valueChanged) XCTAssertTrue(`switch`.rex_on.value) } } diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index d08767f..a6a4baa 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -24,8 +24,7 @@ class UITableViewCellTests: XCTestCase { label.rex_text <~ titleProperty .producer - .map(Optional.init) // TODO: Remove in the future, binding with optionals will be available soon in RAC 5. Reference: https://github.com/ReactiveCocoa/ReactiveCocoa/pull/2852 - .takeUntil(cell.rex_prepareForReuse) + .take(until: cell.rex_prepareForReuse) XCTAssertEqual(label.text, "John") diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index 8255e93..01ef6ac 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -20,16 +20,16 @@ class UITableViewHeaderFooterViewTests: XCTestCase { header.rex_hidden <~ hiddenProperty .producer - .takeUntil(header.rex_prepareForReuse) + .take(until: header.rex_prepareForReuse) - XCTAssertFalse(header.hidden) + XCTAssertFalse(header.isHidden) hiddenProperty <~ SignalProducer(value: true) - XCTAssertTrue(header.hidden) + XCTAssertTrue(header.isHidden) header.prepareForReuse() hiddenProperty <~ SignalProducer(value: false) - XCTAssertTrue(header.hidden) + XCTAssertTrue(header.isHidden) } } diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index fee8e85..588f4eb 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -13,10 +13,10 @@ import XCTest class UITextFieldTests: XCTestCase { func testTextProperty() { - let expectation = self.expectationWithDescription("Expected `rex_text`'s value to equal to the textField's text") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textField's text") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } - let textField = UITextField(frame: CGRectZero) + let textField = UITextField(frame: CGRect.zero) textField.text = "Test" textField.rex_text.signal.observeNext { text in @@ -25,7 +25,7 @@ class UITextFieldTests: XCTestCase { } #if os(iOS) - textField.sendActionsForControlEvents(.EditingChanged) + textField.sendActions(for: .editingChanged) #else NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) #endif diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 2ae4fc5..6374fe5 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -13,10 +13,10 @@ import XCTest class UITextViewTests: XCTestCase { func testTextProperty() { - let expectation = self.expectationWithDescription("Expected `rex_text`'s value to equal to the textViews's text") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textViews's text") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } - let textView = UITextView(frame: CGRectZero) + let textView = UITextView(frame: CGRect.zero) textView.text = "Test" textView.rex_text.startWithNext { text in @@ -24,6 +24,6 @@ class UITextViewTests: XCTestCase { expectation.fulfill() } - NSNotificationCenter.defaultCenter().postNotificationName(UITextViewTextDidChangeNotification, object: textView) + NotificationCenter.default.post(name: NSNotification.Name.UITextViewTextDidChange, object: textView) } } diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 04bf211..9d371e2 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -22,8 +22,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidDisappear() { - let expectation = self.expectationWithDescription("Expected rex_viewDidDisappear to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_viewDidDisappear to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -37,8 +37,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillDisappear() { - let expectation = self.expectationWithDescription("Expected rex_viewWillDisappear to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_viewWillDisappear to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -52,8 +52,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidAppear() { - let expectation = self.expectationWithDescription("Expected rex_viewDidAppear to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_viewDidAppear to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -67,8 +67,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillAppear() { - let expectation = self.expectationWithDescription("Expected rex_viewWillAppear to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_viewWillAppear to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -82,8 +82,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_property() { - let expectation = self.expectationWithDescription("Expected rex_dismissModally to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -97,8 +97,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_cocoaDismiss() { - let expectation = self.expectationWithDescription("Expected rex_dismissModally to be triggered") - defer { self.waitForExpectationsWithTimeout(2, handler: nil) } + let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(withTimeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -107,6 +107,6 @@ class UIViewControllerTests: XCTestCase { expectation.fulfill() } - viewController.dismissViewControllerAnimated(true, completion: nil) + viewController.dismiss(animated: true, completion: nil) } } diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index acd51c3..5527177 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -21,7 +21,7 @@ class UIViewTests: XCTestCase { } func testAlphaPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRectZero) + let view = UIView(frame: CGRect.zero) _view = view view.rex_alpha <~ SignalProducer(value: 0.5) @@ -29,28 +29,28 @@ class UIViewTests: XCTestCase { } func testHiddenPropertyDoesntCreateRetainCycle() { - let view = UIView(frame: CGRectZero) + let view = UIView(frame: CGRect.zero) _view = view view.rex_hidden <~ SignalProducer(value: true) - XCTAssert(_view?.hidden == true) + XCTAssert(_view?.isHidden == true) } func testHiddenProperty() { - let view = UIView(frame: CGRectZero) - view.hidden = true + let view = UIView(frame: CGRect.zero) + view.isHidden = true let (pipeSignal, observer) = Signal.pipe() view.rex_hidden <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(view.hidden) + XCTAssertTrue(view.isHidden) observer.sendNext(false) - XCTAssertFalse(view.hidden) + XCTAssertFalse(view.isHidden) } func testAlphaProperty() { - let view = UIView(frame: CGRectZero) + let view = UIView(frame: CGRect.zero) view.alpha = 0.0 let firstChange = CGFloat(0.5) @@ -66,15 +66,15 @@ class UIViewTests: XCTestCase { } func testUserInteractionEnabledProperty() { - let view = UIView(frame: CGRectZero) - view.userInteractionEnabled = true + let view = UIView(frame: CGRect.zero) + view.isUserInteractionEnabled = true let (pipeSignal, observer) = Signal.pipe() view.rex_userInteractionEnabled <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(view.userInteractionEnabled) + XCTAssertTrue(view.isUserInteractionEnabled) observer.sendNext(false) - XCTAssertFalse(view.userInteractionEnabled) + XCTAssertFalse(view.isUserInteractionEnabled) } } From 98364eee9b55e8b37a180109fe14e9c11f890bac Mon Sep 17 00:00:00 2001 From: Anders Date: Sun, 10 Jul 2016 14:18:25 +0800 Subject: [PATCH 10/31] Update CI config. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 47dd0d1..180d784 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: objective-c matrix: include: - - osx_image: xcode7.3 + - osx_image: xcode8 env: global: From 9ebd18a56a729030eedd0b26517a9707c6484d33 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 11 Jul 2016 10:27:06 +0800 Subject: [PATCH 11/31] Rename a few more APIs. --- Source/Action.swift | 4 ++-- Source/Foundation/Data.swift | 4 ++-- Source/Foundation/NSObject.swift | 2 +- Source/Foundation/UserDefaults.swift | 2 +- Source/Signal.swift | 2 +- Source/SignalProducer.swift | 2 +- Source/UIKit/UIViewController.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 2 +- Tests/SignalTests.swift | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Action.swift b/Source/Action.swift index 8982d41..d7ff5e5 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -43,11 +43,11 @@ extension CocoaAction { /// Creates a producer for the `enabled` state of a CocoaAction. public var rex_enabledProducer: SignalProducer { - return rex_producerForKeyPath("enabled") + return rex_producer(forKeyPath: #keyPath(CocoaAction.isEnabled)) } /// Creates a producer for the `executing` state of a CocoaAction. public var rex_executingProducer: SignalProducer { - return rex_producerForKeyPath("executing") + return rex_producer(forKeyPath: #keyPath(CocoaAction.isExecuting)) } } diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index 91d5403..53aff66 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -10,7 +10,7 @@ import ReactiveCocoa extension Data { /// Read the data at the URL, sending the result or an error. - public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { + public static func rex_data(contentsOf url: URL, options: Data.ReadingOptions = []) -> SignalProducer { return SignalProducer { observer, disposable in do { let data = try Data(contentsOf: url, options: options) @@ -25,7 +25,7 @@ extension Data { extension NSData { /// Read the data at the URL, sending the result or an error. - public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { + public static func rex_data(contentsOf url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { return SignalProducer { observer, disposable in do { let data = try NSData(contentsOf: url, options: options) diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 5747ef1..531085f 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -15,7 +15,7 @@ extension NSObject { /// /// Swift classes deriving `NSObject` must declare properties as `dynamic` for /// them to work with KVO. However, this is not recommended practice. - public func rex_producerForKeyPath(_ keyPath: String) -> SignalProducer { + public func rex_producer(forKeyPath keyPath: String) -> SignalProducer { return self.rac_values(forKeyPath: keyPath, observer: nil) .toSignalProducer() .map { $0 as! T } diff --git a/Source/Foundation/UserDefaults.swift b/Source/Foundation/UserDefaults.swift index 73282cd..c0a9e0b 100644 --- a/Source/Foundation/UserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -14,7 +14,7 @@ extension UserDefaults { /// by casting to NSObject and checking for equality. If the values aren't /// convertible this will generate events whenever _any_ value in NSUserDefaults /// changes. - public func rex_valueForKey(_ key: String) -> SignalProducer { + public func rex_value(forKey key: String) -> SignalProducer { let center = NotificationCenter.default let initial = object(forKey: key) diff --git a/Source/Signal.swift b/Source/Signal.swift index d953075..723d80e 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -82,7 +82,7 @@ extension SignalProtocol { /// /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. - public func muteFor(_ interval: TimeInterval, clock: DateSchedulerProtocol) -> Signal { + public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> Signal { precondition(interval > 0) var expires = clock.currentDate diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index e897c47..69dbd79 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -85,7 +85,7 @@ extension SignalProducerProtocol { /// This operator could be used to coalesce multiple notifications in a short time /// frame by only showing the first one. public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> SignalProducer { - return lift { $0.muteFor(interval, clock: clock) } + return lift { $0.mute(for: interval, clock: clock) } } /// Delays the start of the producer by `interval` on the provided scheduler. diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index b6ab1ab..fc6bcea 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -35,7 +35,7 @@ extension UIViewController { return triggerForSelector(#selector(UIViewController.viewWillAppear(_:))) } - private func triggerForSelector(_ selector: Selector) -> Signal<(), NoError> { + private func trigger(for selector: Selector) -> Signal<(), NoError> { return self .rac_signal(for: selector) .rex_toTriggerSignal() diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 61e793e..83cee3a 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -16,7 +16,7 @@ final class NSObjectTests: XCTestCase { let object = Object() var value: String = "" - object.rex_producerForKeyPath("string").startWithNext { value = $0 } + object.rex_producer(forKeyPath: "string").startWithNext { value = $0 } XCTAssertEqual(value, "foo") object.string = "bar" diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index d009f32..350e980 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -136,7 +136,7 @@ final class SignalTests: XCTestCase { var value = -1 signal - .muteFor(1, clock: scheduler) + .mute(for: 1, clock: scheduler) .observeNext { value = $0 } scheduler.schedule { observer.sendNext(1) } @@ -168,7 +168,7 @@ final class SignalTests: XCTestCase { var failed = false signal - .muteFor(1, clock: scheduler) + .mute(for: 1, clock: scheduler) .observe(Observer( next: { value = $0 }, failed: { _ in failed = true } From 7d8958fb072fc89c38c481b2295218ee1d143fb0 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 11 Jul 2016 10:27:33 +0800 Subject: [PATCH 12/31] Mark a few APIs unavailable. --- Deprecations+Removals.swift | 45 +++++++++++++++++++++++++++++++++++ Rex.xcodeproj/project.pbxproj | 10 ++++++++ 2 files changed, 55 insertions(+) create mode 100644 Deprecations+Removals.swift diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift new file mode 100644 index 0000000..5be18a9 --- /dev/null +++ b/Deprecations+Removals.swift @@ -0,0 +1,45 @@ +// MARK: Renamed APIs in Swift 3.0 +import ReactiveCocoa +import enum Result.NoError + +extension SignalProtocol { + @available(*, unavailable, renamed:"mute(for:clock:)") + public func muteFor(_ interval: TimeInterval, clock: DateSchedulerProtocol) -> Signal { fatalError() } + + @available(*, unavailable, renamed:"timeout(after:with:on:)") + public func timeoutAfter(_ interval: TimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerProtocol) -> Signal { fatalError() } +} + +extension SignalProducerProtocol { + @available(*, unavailable, renamed:"mute(for:clock:)") + public func muteFor(_ interval: TimeInterval, clock: DateSchedulerProtocol) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"timeout(after:with:on:)") + public func timeoutAfter(_ interval: TimeInterval, withEvent event: Event, onScheduler scheduler: DateSchedulerProtocol) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed:"group(by:)") + public func groupBy(_ grouping: (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { fatalError() } + + @available(*, unavailable, renamed:"defer(by:on:)") + public func deferred(_ interval: TimeInterval, onScheduler scheduler: DateSchedulerProtocol) -> SignalProducer { fatalError() } +} + +extension UserDefaults { + @available(*, unavailable, renamed:"rex_value(forKey:)") + public func rex_valueForKey(_ key: String) -> SignalProducer { fatalError() } +} + +extension NSObject { + @available(*, unavailable, renamed:"rex_producer(forKeyPath:)") + public func rex_producerForKeyPath(_ keyPath: String) -> SignalProducer { fatalError() } +} + +extension Data { + @available(*, unavailable, renamed:"rex_data(contentsOf:options:)") + public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { fatalError() } +} + +extension NSData { + @available(*, unavailable, renamed:"rex_data(contentsOf:options:)") + public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { fatalError() } +} diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index ae90df0..efb29c1 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,10 @@ 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 */; }; + 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; + 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; + 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; + 9A5492151D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */; }; 9DA915A61CA63046003723B9 /* UIDatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */; }; C72CF3E51CBF188A00E19897 /* RACSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72CF3E41CBF188A00E19897 /* RACSignal.swift */; }; @@ -234,6 +238,7 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControlTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButtonTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItemTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = SOURCE_ROOT; }; 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePicker.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePickerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RACSignal.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -390,6 +395,7 @@ D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, + 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */, ); path = Source; sourceTree = ""; @@ -832,6 +838,7 @@ C72CF3E51CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, + 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */, @@ -885,6 +892,7 @@ D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, + 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, 7D0DABAA1CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, ); @@ -930,6 +938,7 @@ D8715D9F1C210FF9005F4191 /* Signal.swift in Sources */, C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */, D8715DA41C21107F005F4191 /* Data.swift in Sources */, + 9A5492151D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, ); @@ -942,6 +951,7 @@ D8715DBB1C2112D1005F4191 /* Property.swift in Sources */, D8715DCA1C211553005F4191 /* UILabel.swift in Sources */, 7DCF5B341CC80D78004AEE75 /* UICollectionReusableView.swift in Sources */, + 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, D8715DCB1C211553005F4191 /* UIImageView.swift in Sources */, C7932E841C4B41E100086F3C /* UITextField.swift in Sources */, 7DBD48F41CC8141D0077AD4F /* Reusable.swift in Sources */, From 356d839a7c6bd4ed908ef858d4c22d0610e4cea1 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 11 Jul 2016 10:44:56 +0800 Subject: [PATCH 13/31] Update `UIViewController`. --- Source/UIKit/UIViewController.swift | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index fc6bcea..ca3aa48 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -14,36 +14,36 @@ extension UIViewController { /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidDisappear` is called public var rex_viewDidDisappear: Signal<(), NoError> { - return triggerForSelector(#selector(UIViewController.viewDidDisappear(_:))) + return trigger(for: #selector(UIViewController.viewDidDisappear(_:))) } - + /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillDisappear` is called public var rex_viewWillDisappear: Signal<(), NoError> { - return triggerForSelector(#selector(UIViewController.viewWillDisappear(_:))) + return trigger(for: #selector(UIViewController.viewWillDisappear(_:))) } - + /// Returns a `Signal`, that will be triggered /// when `self`'s `viewDidAppear` is called public var rex_viewDidAppear: Signal<(), NoError> { - return triggerForSelector(#selector(UIViewController.viewDidAppear(_:))) + return trigger(for: #selector(UIViewController.viewDidAppear(_:))) } - + /// Returns a `Signal`, that will be triggered /// when `self`'s `viewWillAppear` is called public var rex_viewWillAppear: Signal<(), NoError> { - return triggerForSelector(#selector(UIViewController.viewWillAppear(_:))) + return trigger(for: #selector(UIViewController.viewWillAppear(_:))) } - + private func trigger(for selector: Selector) -> Signal<(), NoError> { return self .rac_signal(for: selector) .rex_toTriggerSignal() } - + public typealias DismissingCompletion = ((Void) -> Void)? public typealias DismissingInformation = (animated: Bool, completion: DismissingCompletion)? - + /// Wraps a viewController's `dismissViewControllerAnimated` function in a bindable property. /// It mimics the same input as `dismissViewControllerAnimated`: a `Bool` flag for the animation /// and a `(Void -> Void)?` closure for `completion`. @@ -55,21 +55,21 @@ extension UIViewController { /// The dismissal observation can be made either with binding (example above) /// or `viewController.dismissViewControllerAnimated(true, completion: nil)` public var rex_dismissAnimated: MutableProperty { - + let initial: (UIViewController) -> DismissingInformation = { _ in nil } let setter: (UIViewController, DismissingInformation) -> Void = { host, dismissingInfo in - + guard let unwrapped = dismissingInfo else { return } host.dismiss(animated: unwrapped.animated, completion: unwrapped.completion) } - + let property = associatedProperty(self, key: &dismissModally, initial: initial, setter: setter) { property in property <~ self.rac_signal(for: #selector(UIViewController.dismiss)) .take { _ in property.value != nil } .rex_toTriggerSignal() .map { _ in return nil } } - + return property } } From 830088f7c517d7e316a814f0e2b735c79aef5e6a Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 11 Jul 2016 20:58:34 +0800 Subject: [PATCH 14/31] Share `Data`'s implementation of `rex_data` with `NSData`. --- Source/Foundation/Data.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index 53aff66..dc5bf71 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -26,14 +26,6 @@ extension Data { extension NSData { /// Read the data at the URL, sending the result or an error. public static func rex_data(contentsOf url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { - return SignalProducer { observer, disposable in - do { - let data = try NSData(contentsOf: url, options: options) - observer.sendNext(data) - observer.sendCompleted() - } catch { - observer.sendFailed(error as NSError) - } - } + return Data.rex_data(contentsOf: url, options: options).map { $0 as NSData } } } From e164c8833d89277e06daa2a8de19b45dea8bba51 Mon Sep 17 00:00:00 2001 From: Anders Date: Tue, 12 Jul 2016 11:54:01 +0800 Subject: [PATCH 15/31] Targets the `RAC5` branch of ReactiveCocoa. --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 87745f3..167b0e0 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "RAC5-swift3" +github "ReactiveCocoa/ReactiveCocoa" "RAC5" diff --git a/Cartfile.resolved b/Cartfile.resolved index a3bf652..b6eef42 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "2fe88d1d41615ed060489e8cd06fc1b3e8f0ca53" -github "ReactiveCocoa/ReactiveCocoa" "3073a487acd53008a41b89ef6512040a1347212d" +github "antitypical/Result" "7d9c3ac21f22aaece85d37a0fc55ebd1eddf4df4" +github "ReactiveCocoa/ReactiveCocoa" "c495401c16fe4c4d495563b5520feba0cf8d53f2" From 70e145558215c0a1d54d59fce4219f0e9899c410 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 14 Jul 2016 15:55:38 +0800 Subject: [PATCH 16/31] Update dependencies. --- Cartfile.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index b6eef42..25bf333 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "7d9c3ac21f22aaece85d37a0fc55ebd1eddf4df4" -github "ReactiveCocoa/ReactiveCocoa" "c495401c16fe4c4d495563b5520feba0cf8d53f2" +github "antitypical/Result" "8f1affd40bc2b10a15f56ff6cb7b00639e29cb35" +github "ReactiveCocoa/ReactiveCocoa" "ce2ace70893193a5f2e0eadea89ed3a013d0a8b0" From 0cb7ad302a00596c4cd9f6b9840fe15479134ce6 Mon Sep 17 00:00:00 2001 From: Anders Date: Thu, 14 Jul 2016 16:22:41 +0800 Subject: [PATCH 17/31] Update for the latest `RAC5` commit. --- Source/UIKit/UIButton.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index 81bda04..c322971 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -20,7 +20,7 @@ extension UIButton { let property = MutableProperty(initial) property.producer - .combinePrevious(initial: initial) + .combinePrevious(initial) .startWithNext { [weak host] previous, next in host?.removeTarget(previous, action: CocoaAction.selector, for: .touchUpInside) host?.addTarget(next, action: CocoaAction.selector, for: .touchUpInside) From 8889b27ae7cbb04c54231cb9f7bd35ac09fbe5ba Mon Sep 17 00:00:00 2001 From: Anders Date: Sat, 16 Jul 2016 23:35:29 +0800 Subject: [PATCH 18/31] Update the resolved Cartfile. --- Cartfile.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 25bf333..0a37349 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "8f1affd40bc2b10a15f56ff6cb7b00639e29cb35" -github "ReactiveCocoa/ReactiveCocoa" "ce2ace70893193a5f2e0eadea89ed3a013d0a8b0" +github "ReactiveCocoa/ReactiveCocoa" "812d978a1d9c1c262c7996e111de30ee4233b463" From 5f076e419f24214500c1dff6d456437dc62010ba Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 18 Jul 2016 21:56:48 +0800 Subject: [PATCH 19/31] Update tvOS specific implementation. --- Source/UIKit/UITextField.swift | 4 ++-- Tests/UIKit/UITextFieldTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 5b6d849..8842995 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -20,8 +20,8 @@ extension UITextField { #else return associatedProperty(self, key: &textKey, initial: getter, setter: setter) { property in property <~ - NSNotificationCenter.defaultCenter() - .rac_notifications(UITextFieldTextDidChangeNotification, object: self) + NotificationCenter.default + .rac_notifications(forName: .UITextFieldTextDidChange, object: self) .filterMap { ($0.object as? UITextField)?.text } } #endif diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 588f4eb..4bc48b7 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -27,7 +27,7 @@ class UITextFieldTests: XCTestCase { #if os(iOS) textField.sendActions(for: .editingChanged) #else - NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) + NotificationCenter.default.post(name: .UITextFieldTextDidChange, object: textField) #endif } } From b04ec8432f88f56873b3ccfddca1729011163d75 Mon Sep 17 00:00:00 2001 From: Anders Date: Fri, 22 Jul 2016 21:56:54 +0800 Subject: [PATCH 20/31] Updated for the latest RAC5 changes. --- Cartfile.resolved | 2 +- Source/Action.swift | 2 +- Source/Property.swift | 40 ++++++++++++++++++++-------------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 0a37349..11be7c6 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "antitypical/Result" "8f1affd40bc2b10a15f56ff6cb7b00639e29cb35" -github "ReactiveCocoa/ReactiveCocoa" "812d978a1d9c1c262c7996e111de30ee4233b463" +github "ReactiveCocoa/ReactiveCocoa" "8213f2e47c3228ad1895bf74c9b6dba541a45aa7" diff --git a/Source/Action.swift b/Source/Action.swift index d7ff5e5..13f9289 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -12,7 +12,7 @@ import enum Result.NoError extension Action { /// Creates an always disabled action. public static var rex_disabled: Action { - return Action(enabledIf: ConstantProperty(false)) { _ in .empty } + return Action(enabledIf: Property(value: false)) { _ in .empty } } /// Whether the action execution was started. diff --git a/Source/Property.swift b/Source/Property.swift index c743c4e..5f7c2af 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -12,33 +12,33 @@ import enum Result.NoError extension PropertyProtocol where Value == Bool { /// The conjunction of `self` and `other`. public func and(_ other: P) -> AndProperty { - return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) + return AndProperty(terms: [Property(self), Property(other)]) } /// The conjunction of `self` and `other`. - public func and(_ other: AnyProperty) -> AndProperty { - return AndProperty(terms: [AnyProperty(self), other]) + public func and(_ other: Property) -> AndProperty { + return AndProperty(terms: [Property(self), other]) } /// The disjunction of `self` and `other`. public func or(_ other: P) -> OrProperty { - return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) + return OrProperty(terms: [Property(self), Property(other)]) } /// The disjunction of `self` and `other`. - public func or(_ other: AnyProperty) -> OrProperty { - return OrProperty(terms: [AnyProperty(self), other]) + public func or(_ other: Property) -> OrProperty { + return OrProperty(terms: [Property(self), other]) } /// A negated property of `self`. public func not() -> NotProperty { - return NotProperty(source: AnyProperty(self), invert: true) + return NotProperty(source: Property(self), invert: true) } } /// Specialized `PropertyType` for the conjuction of a set of boolean properties. -public struct AndProperty: PropertyProtocol { - public let terms: [AnyProperty] +public class AndProperty: PropertyProtocol { + public let terms: [Property] public var value: Bool { return terms.reduce(true) { $0 && $1.value } @@ -60,22 +60,22 @@ public struct AndProperty: PropertyProtocol { /// Creates a new property with an additional conjunctive term. public func and

(_ other: P) -> AndProperty { - return AndProperty(terms: terms + [AnyProperty(other)]) + return AndProperty(terms: terms + [Property(other)]) } /// Creates a new property with an additional conjunctive term. - public func and(_ other: AnyProperty) -> AndProperty { + public func and(_ other: Property) -> AndProperty { return AndProperty(terms: terms + [other]) } - private init(terms: [AnyProperty]) { + private init(terms: [Property]) { self.terms = terms } } /// Specialized `PropertyType` for the disjunction of a set of boolean properties. -public struct OrProperty: PropertyProtocol { - public let terms: [AnyProperty] +public class OrProperty: PropertyProtocol { + public let terms: [Property] public var value: Bool { return terms.reduce(false) { $0 || $1.value } @@ -97,22 +97,22 @@ public struct OrProperty: PropertyProtocol { /// Creates a new property with an additional disjunctive term. public func or

(_ other: P) -> OrProperty { - return OrProperty(terms: terms + [AnyProperty(other)]) + return OrProperty(terms: terms + [Property(other)]) } /// Creates a new property with an additional disjunctive term. - public func or(_ other: AnyProperty) -> OrProperty { + public func or(_ other: Property) -> OrProperty { return OrProperty(terms: terms + [other]) } - private init(terms: [AnyProperty]) { + private init(terms: [Property]) { self.terms = terms } } /// Specialized `PropertyType` for the negation of a boolean property. -public struct NotProperty: PropertyProtocol { - private let source: AnyProperty +public class NotProperty: PropertyProtocol { + private let source: Property private let invert: Bool public var value: Bool { @@ -132,7 +132,7 @@ public struct NotProperty: PropertyProtocol { return NotProperty(source: source, invert: !invert) } - private init(source: AnyProperty, invert: Bool) { + private init(source: Property, invert: Bool) { self.source = source self.invert = invert } From 15dca561b97e71c263c339847e8a1270ca4adc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Fri, 5 Aug 2016 13:28:16 +0200 Subject: [PATCH 21/31] Partial support for Xcode8b4 --- Cartfile.resolved | 4 ++-- Deprecations+Removals.swift | 2 +- Rex.xcodeproj/project.pbxproj | 6 +++++ Source/Foundation/Association.swift | 10 ++++---- Source/Foundation/Data.swift | 2 +- Source/SignalProducer.swift | 16 ++++++------- Source/UIKit/UIActivityIndicatorView.swift | 3 +-- Source/UIKit/UILabel.swift | 2 +- Tests/ActionTests.swift | 2 +- Tests/Foundation/NSObjectTests.swift | 4 ++-- Tests/SignalTests.swift | 2 +- .../UIKit/UIActivityIndicatorViewTests.swift | 4 ++-- Tests/UIKit/UIDatePickerTests.swift | 4 ++-- Tests/UIKit/UILabelTests.swift | 6 ++--- Tests/UIKit/UITextFieldTests.swift | 4 ++-- Tests/UIKit/UITextViewTests.swift | 4 ++-- Tests/UIKit/UIViewControllerTests.swift | 24 +++++++++---------- 17 files changed, 52 insertions(+), 47 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 11be7c6..8251c3b 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "antitypical/Result" "8f1affd40bc2b10a15f56ff6cb7b00639e29cb35" -github "ReactiveCocoa/ReactiveCocoa" "8213f2e47c3228ad1895bf74c9b6dba541a45aa7" +github "jameshurst/Result" "2401fa7584183da17f40335816073bf511fb34b8" +github "ReactiveCocoa/ReactiveCocoa" "b4d757433574f85519bb3253a631955da937106a" diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift index 5be18a9..a6de012 100644 --- a/Deprecations+Removals.swift +++ b/Deprecations+Removals.swift @@ -39,7 +39,7 @@ extension Data { public static func rex_dataWithContentsOfURL(_ url: URL, options: Data.ReadingOptions = []) -> SignalProducer { fatalError() } } -extension NSData { +extension Data { @available(*, unavailable, renamed:"rex_data(contentsOf:options:)") public static func rex_dataWithContentsOfURL(_ url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { fatalError() } } diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index efb29c1..62cea14 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -740,9 +740,11 @@ }; D83457131AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D834571D1AFEE44E0070616A = { CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 0800; }; D8715D931C210F97005F4191 = { CreatedOnToolsVersion = 7.2; @@ -1221,6 +1223,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1248,6 +1251,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1273,6 +1277,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1292,6 +1297,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 8be4d03..56c492c 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,14 +15,14 @@ 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 { +public func associatedProperty(host: AnyObject, keyPath: StaticString) -> MutableProperty { let initial: (AnyObject) -> String = { host in host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) + return associatedProperty(host: host, key: keyPath.utf8Start, initial: initial, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -32,11 +32,11 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host, key: keyPath.utf8Start, initial: { host in + return associatedProperty(host: host, key: keyPath.utf8Start, initial: { host in host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -47,7 +47,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index dc5bf71..8f9527c 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -23,7 +23,7 @@ extension Data { } } -extension NSData { +extension Data { /// Read the data at the URL, sending the result or an error. public static func rex_data(contentsOf url: URL, options: NSData.ReadingOptions = []) -> SignalProducer { return Data.rex_data(contentsOf: url, options: options).map { $0 as NSData } diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index 69dbd79..7d02de4 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -18,7 +18,7 @@ extension SignalProducerProtocol { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in var groups: [Key: Signal.Observer] = [:] - let lock = RecursiveLock() + let lock = NSRecursiveLock() lock.name = "me.neilpa.rex.groupBy" self.start { event in @@ -96,7 +96,7 @@ extension SignalProducerProtocol { } /// Delays retrying on failure by `interval` up to `count` attempts. - public func deferredRetry(interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { + public func deferredRetry(_ interval: TimeInterval, on scheduler: DateSchedulerProtocol, count: Int = .max) -> SignalProducer { precondition(count >= 0) if count == 0 { @@ -205,25 +205,25 @@ private final class ReplayBuffer { private var values: [Value] = [] } -private struct BufferState { +private struct BufferState { /// All values in the buffer. - var values: [Value] = [] + var values: [V] = [] /// Any terminating event sent to the buffer. /// /// This will be nil if termination has not occurred. - var terminationEvent: Event? + var terminationEvent: Event? /// The observers currently attached to the buffered producer, or nil if the /// producer was terminated. - var observers: Bag.Observer>? = Bag() + var observers: Bag.Observer>? = Bag() /// The set of unused replay token identifiers. - var replayBuffers: Bag> = Bag() + var replayBuffers: Bag> = Bag() /// Appends a new value to the buffer, trimming it down to the given capacity /// if necessary. - mutating func add(_ value: Value, upTo capacity: Int) { + mutating func add(_ value: V, upTo capacity: Int) { precondition(capacity >= 0) for buffer in replayBuffers { diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 179b795..6833ef6 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -15,7 +15,7 @@ extension UIActivityIndicatorView { /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. public var rex_animating: MutableProperty { - return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in + return associatedProperty(host: self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in if animating { host.startAnimating() } else { @@ -23,7 +23,6 @@ extension UIActivityIndicatorView { } }) } - } private var animatingKey: UInt8 = 0 diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index 0078d7c..c47746f 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -16,7 +16,7 @@ extension UILabel { } /// Wraps a label's `attributedText` value in a bindable property. - public var rex_attributedText: MutableProperty { + public var rex_attributedText: MutableProperty { return associatedProperty(self, key: &attributedTextKey, initial: { $0.attributedText }, setter: { $0.attributedText = $1 }) } diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index b6d7338..2c4673a 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -13,7 +13,7 @@ import enum Result.NoError final class ActionTests: XCTestCase { - enum TestError: ErrorProtocol { + enum TestError: Error { case unknown } diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index 83cee3a..38ba1de 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -25,8 +25,8 @@ final class NSObjectTests: XCTestCase { func testObjectsWillBeDeallocatedSignal() { - let expectation = self.expectation(withDescription: "Expected timer to send `completed` event when object deallocates") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected timer to send `completed` event when object deallocates") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let object = Object() diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 350e980..2686047 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -186,6 +186,6 @@ final class SignalTests: XCTestCase { } } -enum TestError: ErrorProtocol { +enum TestError: Error { case `default` } diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index ece8aef..df5051d 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -27,8 +27,8 @@ class UIActivityIndicatorTests: XCTestCase { indicatorView.rex_animating <~ SignalProducer(signal: pipeSignal) observer.sendNext(true) - XCTAssertTrue(indicatorView.isAnimating()) + XCTAssertTrue(indicatorView.isAnimating) observer.sendNext(false) - XCTAssertFalse(indicatorView.isAnimating()) + XCTAssertFalse(indicatorView.isAnimating) } } diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 20ac37e..55d9362 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -31,8 +31,8 @@ class UIDatePickerTests: XCTestCase { } func testUpdatePropertyFromPicker() { - let expectation = self.expectation(withDescription: "Expected rex_date to send an event when picker's date value is changed by a UI event") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_date to send an event when picker's date value is changed by a UI event") + defer { self.waitForExpectations(timeout: 2, handler: nil) } picker.rex_date.signal.observeNext { changedDate in XCTAssertEqual(changedDate, self.date) diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index ded0e7d..3e9fcf5 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -71,13 +71,13 @@ class UILabelTests: XCTestCase { } func testTextColorProperty() { - let firstChange = UIColor.red() - let secondChange = UIColor.black() + let firstChange = UIColor.red + let secondChange = UIColor.black let label = UILabel(frame: CGRect.zero) let (pipeSignal, observer) = Signal.pipe() - label.textColor = UIColor.black() + label.textColor = UIColor.black label.rex_textColor <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 4bc48b7..8ce8d83 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -13,8 +13,8 @@ import XCTest class UITextFieldTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textField's text") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textField's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let textField = UITextField(frame: CGRect.zero) textField.text = "Test" diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 6374fe5..d99f0e9 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -13,8 +13,8 @@ import XCTest class UITextViewTests: XCTestCase { func testTextProperty() { - let expectation = self.expectation(withDescription: "Expected `rex_text`'s value to equal to the textViews's text") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected `rex_text`'s value to equal to the textViews's text") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let textView = UITextView(frame: CGRect.zero) textView.text = "Test" diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 9d371e2..315b723 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -22,8 +22,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidDisappear() { - let expectation = self.expectation(withDescription: "Expected rex_viewDidDisappear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewDidDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -37,8 +37,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillDisappear() { - let expectation = self.expectation(withDescription: "Expected rex_viewWillDisappear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewWillDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -52,8 +52,8 @@ class UIViewControllerTests: XCTestCase { func testViewDidAppear() { - let expectation = self.expectation(withDescription: "Expected rex_viewDidAppear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewDidAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -67,8 +67,8 @@ class UIViewControllerTests: XCTestCase { func testViewWillAppear() { - let expectation = self.expectation(withDescription: "Expected rex_viewWillAppear to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_viewWillAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -82,8 +82,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_property() { - let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -97,8 +97,8 @@ class UIViewControllerTests: XCTestCase { func testDismissViewController_via_cocoaDismiss() { - let expectation = self.expectation(withDescription: "Expected rex_dismissModally to be triggered") - defer { self.waitForExpectations(withTimeout: 2, handler: nil) } + let expectation = self.expectation(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController From c2404eb4c0f4e5eacebaede8aa76ba7275422179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Fri, 5 Aug 2016 13:35:56 +0200 Subject: [PATCH 22/31] Fix Build on Xcode8b4 --- Source/Foundation/Association.swift | 10 +++++----- Source/UIKit/UIActivityIndicatorView.swift | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 56c492c..8be4d03 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -15,14 +15,14 @@ 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 { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> MutableProperty { let initial: (AnyObject) -> String = { host in host.value(forKeyPath: String(keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host: host, key: keyPath.utf8Start, initial: initial, setter: setter) + return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial @@ -32,11 +32,11 @@ public func associatedProperty(host: AnyObject, keyPath: StaticString) -> Mutabl /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(keyPath)) } - return associatedProperty(host: host, key: keyPath.utf8Start, initial: { host in + return associatedProperty(host, key: keyPath.utf8Start, initial: { host in host.value(forKeyPath: String(keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -47,7 +47,7 @@ public func associatedProperty(host: AnyObject, keyPath: StaticStr /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 6833ef6..b2579b1 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -14,15 +14,15 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. - public var rex_animating: MutableProperty { - return associatedProperty(host: self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in - if animating { - host.startAnimating() - } else { - host.stopAnimating() - } - }) - } +// public var rex_animating: MutableProperty { +// return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in +// if animating { +// host.startAnimating() +// } else { +// host.stopAnimating() +// } +// }) +// } } private var animatingKey: UInt8 = 0 From b3a207ea7ebe9906d4ba7405a6b7d6c5d963de43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Mon, 8 Aug 2016 14:45:33 +0200 Subject: [PATCH 23/31] Update ReactiveCocoa Dependency (damm branch fuckers) --- Cartfile | 2 +- Cartfile.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 167b0e0..38f3bb3 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" "RAC5" +github "ReactiveCocoa/ReactiveCocoa" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index 8251c3b..006ec05 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "jameshurst/Result" "2401fa7584183da17f40335816073bf511fb34b8" -github "ReactiveCocoa/ReactiveCocoa" "b4d757433574f85519bb3253a631955da937106a" +github "antitypical/Result" "3.0.0-alpha.2" +github "ReactiveCocoa/ReactiveCocoa" "d98489ae97b020ccdd9a8c8ccad2b67429817669" From 012e5195b31bd0dea02b7735cedd3858410e730a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Varela?= Date: Tue, 9 Aug 2016 10:47:32 +0200 Subject: [PATCH 24/31] fix remaining warning on Xcode8b4 --- Source/Foundation/UserDefaults.swift | 2 +- Source/UIKit/UIActivityIndicatorView.swift | 18 +++++++++--------- Tests/PropertyTests.swift | 4 ++-- Tests/SignalProducerTests.swift | 4 ++-- Tests/UIKit/UILabelTests.swift | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Foundation/UserDefaults.swift b/Source/Foundation/UserDefaults.swift index c0a9e0b..abc1393 100644 --- a/Source/Foundation/UserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -28,7 +28,7 @@ extension UserDefaults { return SignalProducer(value: initial) .concat(changes) .skipRepeats { previous, next in - if let previous = previous as? NSObject, next = next as? NSObject { + if let previous = previous as? NSObject, let next = next as? NSObject { return previous == next } return false diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index b2579b1..85d5848 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -14,15 +14,15 @@ extension UIActivityIndicatorView { /// Wraps an indicator's `isAnimating()` state in a bindable property. /// Setting a new value to the property would call `startAnimating()` or /// `stopAnimating()` depending on the value. -// public var rex_animating: MutableProperty { -// return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in -// if animating { -// host.startAnimating() -// } else { -// host.stopAnimating() -// } -// }) -// } + public var rex_animating: MutableProperty { + return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in + if animating { + host.startAnimating() + } else { + host.stopAnimating() + } + }) + } } private var animatingKey: UInt8 = 0 diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 6faedac..7ee9312 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -32,7 +32,7 @@ final class PropertyTests: XCTestCase { XCTAssertTrue(current!) let (signal, pipe) = Signal.pipe() - let and2 = and.and(AnyProperty(initial: false, then: signal)) + let and2 = and.and(Property(initial: false, then: signal)) and2.producer.startWithNext { current = $0 } XCTAssertFalse(and2.value) @@ -62,7 +62,7 @@ final class PropertyTests: XCTestCase { XCTAssertFalse(current!) let (signal, pipe) = Signal.pipe() - let or2 = or.or(AnyProperty(initial: true, then: signal)) + let or2 = or.or(Property(initial: true, then: signal)) or2.producer.startWithNext { current = $0 } XCTAssertTrue(or2.value) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 997ad37..f2cbff6 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -98,7 +98,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var completed = false producer - .deferredRetry(interval: 1, on: scheduler) + .deferredRetry(1, on: scheduler) .start(Observer( next: { value = $0 }, completed: { completed = true } @@ -136,7 +136,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var failed = false producer - .deferredRetry(interval: 1, on: scheduler, count: 2) + .deferredRetry(1, on: scheduler, count: 2) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 3e9fcf5..056d1ec 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -50,18 +50,18 @@ class UILabelTests: XCTestCase { let label = UILabel(frame: CGRect.zero) _label = label - label.rex_attributedText <~ SignalProducer(value: AttributedString(string: "Test")) + label.rex_attributedText <~ SignalProducer(value: NSAttributedString(string: "Test")) XCTAssert(_label?.attributedText?.string == "Test") } func testAttributedTextProperty() { - let firstChange = AttributedString(string: "first") - let secondChange = AttributedString(string: "second") + let firstChange = NSAttributedString(string: "first") + let secondChange = NSAttributedString(string: "second") let label = UILabel(frame: CGRect.zero) - label.attributedText = AttributedString(string: "") + label.attributedText = NSAttributedString(string: "") - let (pipeSignal, observer) = Signal.pipe() + let (pipeSignal, observer) = Signal.pipe() label.rex_attributedText <~ SignalProducer(signal: pipeSignal) observer.sendNext(firstChange) From ee70b743efd2e3eabc4c8c722d95ad87da1655eb Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:50:15 +0100 Subject: [PATCH 25/31] Updated for the ReactiveSwift spin-off. --- Cartfile.resolved | 5 +- Deprecations+Removals.swift | 1 + Rex.xcodeproj/project.pbxproj | 75 +++++++++++++------ .../xcshareddata/xcschemes/Rex-Mac.xcscheme | 16 ++-- Source/Action.swift | 1 + Source/AppKit/NSTextField.swift | 1 + Source/Foundation/Association.swift | 1 + Source/Foundation/Data.swift | 2 +- Source/Foundation/NSObject.swift | 1 + Source/Foundation/UserDefaults.swift | 1 + Source/Property.swift | 1 + Source/RACSignal.swift | 1 + Source/Reusable.swift | 1 + Source/Signal.swift | 1 + Source/SignalProducer.swift | 1 + Source/UIKit/UIActivityIndicatorView.swift | 1 + Source/UIKit/UIBarButtonItem.swift | 1 + Source/UIKit/UIBarItem.swift | 1 + Source/UIKit/UIButton.swift | 1 + Source/UIKit/UIControl.swift | 1 + Source/UIKit/UIDatePicker.swift | 1 + Source/UIKit/UIImageView.swift | 1 + Source/UIKit/UILabel.swift | 1 + Source/UIKit/UIProgressView.swift | 3 +- Source/UIKit/UISegmentedControl.swift | 1 + Source/UIKit/UISwitch.swift | 1 + Source/UIKit/UITextField.swift | 2 + Source/UIKit/UITextView.swift | 1 + Source/UIKit/UIView.swift | 1 + Source/UIKit/UIViewController.swift | 1 + Tests/ActionTests.swift | 1 + Tests/Foundation/NSObjectTests.swift | 1 + Tests/PropertyTests.swift | 1 + Tests/SignalProducerTests.swift | 1 + Tests/SignalTests.swift | 1 + .../UIKit/UIActivityIndicatorViewTests.swift | 1 + Tests/UIKit/UIBarButtonItemTests.swift | 1 + Tests/UIKit/UIButtonTests.swift | 1 + .../UIKit/UICollectionReusableViewTests.swift | 2 +- Tests/UIKit/UIControlTests.swift | 1 + Tests/UIKit/UIDatePickerTests.swift | 1 + Tests/UIKit/UIImageViewTests.swift | 1 + Tests/UIKit/UILabelTests.swift | 1 + Tests/UIKit/UIProgressViewTests.swift | 1 + Tests/UIKit/UISegmentedControlTests.swift | 1 + Tests/UIKit/UISwitchTests.swift | 1 + Tests/UIKit/UITableViewCellTests.swift | 2 +- .../UITableViewHeaderFooterViewTests.swift | 2 +- Tests/UIKit/UITextFieldTests.swift | 1 + Tests/UIKit/UITextViewTests.swift | 1 + Tests/UIKit/UIViewControllerTests.swift | 1 + Tests/UIKit/UIViewTests.swift | 1 + 52 files changed, 114 insertions(+), 38 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 7acf524..118b995 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,3 @@ -github "antitypical/Result" "3.0.0-alpha.3" -github "ReactiveCocoa/ReactiveCocoa" "b711611ad9906b1bfc5027824f57a2889e34204d" +github "antitypical/Result" "3.0.0" +github "ReactiveCocoa/ReactiveSwift" "598ce311be21911d3d365c6b9c5245fdc9e72394" +github "ReactiveCocoa/ReactiveCocoa" "26997cf6f0eda7d64b76ba82013525e6ab900e8e" diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift index a6de012..5b62761 100644 --- a/Deprecations+Removals.swift +++ b/Deprecations+Removals.swift @@ -1,4 +1,5 @@ // MARK: Renamed APIs in Swift 3.0 +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 5785e32..4ca606f 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,17 @@ 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 */; }; + 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */; }; + 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */; }; + 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; + 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; @@ -60,8 +71,8 @@ CC02C18B1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; CC02C18C1CCA704F0025CC04 /* ActionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC02C1881CCA704C0025CC04 /* ActionTests.swift */; }; D8003E941AFEC3D400D7D3C5 /* Rex.h in Headers */ = {isa = PBXBuildFile; fileRef = D8003E931AFEC3D400D7D3C5 /* Rex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D8003EBD1AFED01000D7D3C5 /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D8003EC21AFED30F00D7D3C5 /* SignalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */; }; @@ -71,7 +82,6 @@ D834572D1AFEE45B0070616A /* Signal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EBC1AFED01000D7D3C5 /* Signal.swift */; }; D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */; }; D83457301AFEE45E0070616A /* SignalProducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */; }; - D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; D83457331AFEE4930070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAE1AFEC68A00D7D3C5 /* Result.framework */; }; D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */; }; D83457361AFEE4B20070616A /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */; }; @@ -184,6 +194,7 @@ dstSubfolderSpec = 16; files = ( D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */, + 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */, D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */, ); name = "Copy Files"; @@ -196,6 +207,7 @@ dstSubfolderSpec = 16; files = ( D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -206,6 +218,7 @@ dstPath = ""; dstSubfolderSpec = 16; files = ( + 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); @@ -238,6 +251,10 @@ 8295FD851B873081007C9000 /* UIControlTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIControlTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD881B873490007C9000 /* UIButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIButtonTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8295FD8B1B873748007C9000 /* UIBarButtonItemTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIBarButtonItemTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; + 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = ""; }; + 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = tvOS/ReactiveSwift.framework; sourceTree = ""; }; + 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = watchOS/ReactiveSwift.framework; sourceTree = ""; }; 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deprecations+Removals.swift"; sourceTree = SOURCE_ROOT; }; 9DA915A31CA6301C003723B9 /* UIDatePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePicker.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 9DA915A51CA63046003723B9 /* UIDatePickerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIDatePickerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -261,7 +278,7 @@ D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveCocoa.framework; sourceTree = ""; }; D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; D86E77A91AFEE646004BF23D /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D86E77AB1AFEE646004BF23D /* Rex.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Rex.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RexTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D86FFBD01B34AD6F001A89B3 /* Association.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Association.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -292,8 +309,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457321AFEE4930070616A /* ReactiveCocoa.framework in Frameworks */, D83457331AFEE4930070616A /* Result.framework in Frameworks */, + 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */, + 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -302,6 +320,7 @@ buildActionMask = 2147483647; files = ( D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, + 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */, D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -310,8 +329,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, D83457361AFEE4B20070616A /* Result.framework in Frameworks */, + 9A0501951D86D6FB006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D83457351AFEE4B20070616A /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -320,6 +340,7 @@ buildActionMask = 2147483647; files = ( D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, + 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */, D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -328,8 +349,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DAA1C2110DA005F4191 /* Result.framework in Frameworks */, + 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D8715DA91C2110DA005F4191 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -337,8 +359,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DC51C211310005F4191 /* Result.framework in Frameworks */, + 9A05019E1D86D759006BBAE8 /* ReactiveSwift.framework in Frameworks */, + D8715DC41C211310005F4191 /* ReactiveCocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -348,6 +371,7 @@ files = ( D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DE71C21170C005F4191 /* Result.framework in Frameworks */, + 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -436,7 +460,7 @@ isa = PBXGroup; children = ( D8003EC71AFEE39000D7D3C5 /* iOS */, - D8003EAB1AFEC67700D7D3C5 /* Mac */, + D8003EAB1AFEC67700D7D3C5 /* macOS */, D8715DB81C21124B005F4191 /* tvOS */, D8715D9C1C210FA1005F4191 /* watchOS */, ); @@ -444,14 +468,16 @@ path = Carthage/Build; sourceTree = ""; }; - D8003EAB1AFEC67700D7D3C5 /* Mac */ = { + D8003EAB1AFEC67700D7D3C5 /* macOS */ = { isa = PBXGroup; children = ( D86E77A91AFEE646004BF23D /* Rex.framework */, - D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */, + D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */, + 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */, D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */, D8003EAE1AFEC68A00D7D3C5 /* Result.framework */, ); + name = macOS; path = Mac; sourceTree = ""; }; @@ -460,6 +486,7 @@ children = ( D86E77AB1AFEE646004BF23D /* Rex.framework */, D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, + 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */, D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, ); @@ -496,6 +523,7 @@ children = ( D8715D941C210F97005F4191 /* Rex.framework */, D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */, + 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */, D8715DA81C2110DA005F4191 /* Result.framework */, ); name = watchOS; @@ -506,6 +534,7 @@ children = ( D8715DB01C21123E005F4191 /* Rex.framework */, D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */, + 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */, D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, D8715DC31C211310005F4191 /* Result.framework */, ); @@ -593,9 +622,9 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */ = { + D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */; + buildConfigurationList = D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */; buildPhases = ( D8003E891AFEC3D400D7D3C5 /* Sources */, D8003E8A1AFEC3D400D7D3C5 /* Frameworks */, @@ -606,14 +635,14 @@ ); dependencies = ( ); - name = "Rex-Mac"; + name = "Rex-macOS"; productName = Rex; productReference = D86E77A91AFEE646004BF23D /* Rex.framework */; productType = "com.apple.product-type.framework"; }; - D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */ = { + D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */; + buildConfigurationList = D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */; buildPhases = ( D8003E951AFEC3D400D7D3C5 /* Sources */, D8003E961AFEC3D400D7D3C5 /* Frameworks */, @@ -625,9 +654,9 @@ dependencies = ( D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */, ); - name = "RexTests-Mac"; + name = "RexTests-macOS"; productName = RexTests; - productReference = D86E77AA1AFEE646004BF23D /* RexTests-Mac.xctest */; + productReference = D86E77AA1AFEE646004BF23D /* RexTests-macOS.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; D83457131AFEE44E0070616A /* Rex-iOS */ = { @@ -774,8 +803,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */, - D8003E981AFEC3D400D7D3C5 /* RexTests-Mac */, + D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */, + D8003E981AFEC3D400D7D3C5 /* RexTests-macOS */, D83457131AFEE44E0070616A /* Rex-iOS */, D834571D1AFEE44E0070616A /* RexTests-iOS */, D8715DAF1C21123E005F4191 /* Rex-tvOS */, @@ -1011,7 +1040,7 @@ /* Begin PBXTargetDependency section */ D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */; + target = D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */; targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; }; D83457211AFEE44E0070616A /* PBXTargetDependency */ = { @@ -1451,7 +1480,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-Mac" */ = { + D8003EA41AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "Rex-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA51AFEC3D400D7D3C5 /* Debug */, @@ -1460,7 +1489,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-Mac" */ = { + D8003EA71AFEC3D400D7D3C5 /* Build configuration list for PBXNativeTarget "RexTests-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( D8003EA81AFEC3D400D7D3C5 /* Debug */, diff --git a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme index 3a84fae..475a6e0 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -16,7 +16,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -29,8 +29,8 @@ @@ -47,8 +47,8 @@ @@ -58,7 +58,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -80,7 +80,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> @@ -98,7 +98,7 @@ BuildableIdentifier = "primary" BlueprintIdentifier = "D8003E8D1AFEC3D400D7D3C5" BuildableName = "Rex.framework" - BlueprintName = "Rex-Mac" + BlueprintName = "Rex-macOS" ReferencedContainer = "container:Rex.xcodeproj"> diff --git a/Source/Action.swift b/Source/Action.swift index 13f9289..e46eb98 100644 --- a/Source/Action.swift +++ b/Source/Action.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index 4f2ba98..67c3321 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import AppKit import enum Result.NoError diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index c7ce80e..485ab32 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa /// Attaches a `MutableProperty` value to the `host` object using KVC to get the initial diff --git a/Source/Foundation/Data.swift b/Source/Foundation/Data.swift index 8f9527c..4f77d0b 100644 --- a/Source/Foundation/Data.swift +++ b/Source/Foundation/Data.swift @@ -6,7 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // -import ReactiveCocoa +import ReactiveSwift extension Data { /// Read the data at the URL, sending the result or an error. diff --git a/Source/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index 531085f..68e118b 100644 --- a/Source/Foundation/NSObject.swift +++ b/Source/Foundation/NSObject.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Foundation/UserDefaults.swift b/Source/Foundation/UserDefaults.swift index a67e4cc..061e0e2 100644 --- a/Source/Foundation/UserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Property.swift b/Source/Property.swift index a263569..d383145 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 14f4f29..c1220bd 100644 --- a/Source/RACSignal.swift +++ b/Source/RACSignal.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Reusable.swift b/Source/Reusable.swift index a71197f..4fc3dce 100644 --- a/Source/Reusable.swift +++ b/Source/Reusable.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/Signal.swift b/Source/Signal.swift index cfef9b6..2e796ea 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/SignalProducer.swift b/Source/SignalProducer.swift index d436a74..46f08dc 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import enum Result.NoError diff --git a/Source/UIKit/UIActivityIndicatorView.swift b/Source/UIKit/UIActivityIndicatorView.swift index 85d5848..62fc290 100644 --- a/Source/UIKit/UIActivityIndicatorView.swift +++ b/Source/UIKit/UIActivityIndicatorView.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index d2f6960..fd9239f 100644 --- a/Source/UIKit/UIBarButtonItem.swift +++ b/Source/UIKit/UIBarButtonItem.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 5ff61c6..710ee88 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index c322971..82361ec 100644 --- a/Source/UIKit/UIButton.swift +++ b/Source/UIKit/UIButton.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIControl.swift b/Source/UIKit/UIControl.swift index 09ae8d9..c9b5053 100644 --- a/Source/UIKit/UIControl.swift +++ b/Source/UIKit/UIControl.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIDatePicker.swift b/Source/UIKit/UIDatePicker.swift index 3fbbd32..3b14724 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIImageView.swift b/Source/UIKit/UIImageView.swift index db6d09e..75094a7 100644 --- a/Source/UIKit/UIImageView.swift +++ b/Source/UIKit/UIImageView.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UILabel.swift b/Source/UIKit/UILabel.swift index c47746f..a77bcaf 100644 --- a/Source/UIKit/UILabel.swift +++ b/Source/UIKit/UILabel.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIProgressView.swift b/Source/UIKit/UIProgressView.swift index 52119d4..871d287 100644 --- a/Source/UIKit/UIProgressView.swift +++ b/Source/UIKit/UIProgressView.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit @@ -16,4 +17,4 @@ extension UIProgressView { } } -private var progressKey: UInt8 = 0 \ No newline at end of file +private var progressKey: UInt8 = 0 diff --git a/Source/UIKit/UISegmentedControl.swift b/Source/UIKit/UISegmentedControl.swift index acd990a..4a7f4a1 100644 --- a/Source/UIKit/UISegmentedControl.swift +++ b/Source/UIKit/UISegmentedControl.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UISwitch.swift b/Source/UIKit/UISwitch.swift index e1b6612..9c06c48 100644 --- a/Source/UIKit/UISwitch.swift +++ b/Source/UIKit/UISwitch.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UITextField.swift b/Source/UIKit/UITextField.swift index 8842995..4beb536 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -6,6 +6,8 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index c78df6d..7d02851 100644 --- a/Source/UIKit/UITextView.swift +++ b/Source/UIKit/UITextView.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import enum Result.NoError diff --git a/Source/UIKit/UIView.swift b/Source/UIKit/UIView.swift index 2284087..51bb647 100644 --- a/Source/UIKit/UIView.swift +++ b/Source/UIKit/UIView.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit diff --git a/Source/UIKit/UIViewController.swift b/Source/UIKit/UIViewController.swift index ca3aa48..add6634 100644 --- a/Source/UIKit/UIViewController.swift +++ b/Source/UIKit/UIViewController.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import Result import ReactiveCocoa import UIKit diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index 2c4673a..87f4135 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -7,6 +7,7 @@ // @testable import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/Foundation/NSObjectTests.swift b/Tests/Foundation/NSObjectTests.swift index a453989..e04158f 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index 7ee9312..a5429c7 100644 --- a/Tests/PropertyTests.swift +++ b/Tests/PropertyTests.swift @@ -7,6 +7,7 @@ // @testable import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index f2cbff6..41094dc 100644 --- a/Tests/SignalProducerTests.swift +++ b/Tests/SignalProducerTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/SignalTests.swift b/Tests/SignalTests.swift index 2686047..54a4189 100644 --- a/Tests/SignalTests.swift +++ b/Tests/SignalTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index df5051d..67e7edd 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result diff --git a/Tests/UIKit/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index d20553b..97e79d9 100644 --- a/Tests/UIKit/UIBarButtonItemTests.swift +++ b/Tests/UIKit/UIBarButtonItemTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIButtonTests.swift b/Tests/UIKit/UIButtonTests.swift index a03a28a..1be613d 100644 --- a/Tests/UIKit/UIButtonTests.swift +++ b/Tests/UIKit/UIButtonTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift index 9c4fea9..68b3c93 100644 --- a/Tests/UIKit/UICollectionReusableViewTests.swift +++ b/Tests/UIKit/UICollectionReusableViewTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UICollectionReusableViewTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UIControlTests.swift b/Tests/UIKit/UIControlTests.swift index 07f35d0..9715c80 100644 --- a/Tests/UIKit/UIControlTests.swift +++ b/Tests/UIKit/UIControlTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIDatePickerTests.swift b/Tests/UIKit/UIDatePickerTests.swift index 55d9362..f7dac8f 100644 --- a/Tests/UIKit/UIDatePickerTests.swift +++ b/Tests/UIKit/UIDatePickerTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIImageViewTests.swift b/Tests/UIKit/UIImageViewTests.swift index b8f4e0f..ccb1fcc 100644 --- a/Tests/UIKit/UIImageViewTests.swift +++ b/Tests/UIKit/UIImageViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UILabelTests.swift b/Tests/UIKit/UILabelTests.swift index 056d1ec..8c826c3 100644 --- a/Tests/UIKit/UILabelTests.swift +++ b/Tests/UIKit/UILabelTests.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIProgressViewTests.swift b/Tests/UIKit/UIProgressViewTests.swift index 9e81afd..c8300d9 100644 --- a/Tests/UIKit/UIProgressViewTests.swift +++ b/Tests/UIKit/UIProgressViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UISegmentedControlTests.swift b/Tests/UIKit/UISegmentedControlTests.swift index d7c7bfa..9a9f8e7 100644 --- a/Tests/UIKit/UISegmentedControlTests.swift +++ b/Tests/UIKit/UISegmentedControlTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result diff --git a/Tests/UIKit/UISwitchTests.swift b/Tests/UIKit/UISwitchTests.swift index 5098ff7..684914e 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result diff --git a/Tests/UIKit/UITableViewCellTests.swift b/Tests/UIKit/UITableViewCellTests.swift index a6a4baa..2ff7cca 100644 --- a/Tests/UIKit/UITableViewCellTests.swift +++ b/Tests/UIKit/UITableViewCellTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UITableViewCellTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift index 01ef6ac..20eb2ee 100644 --- a/Tests/UIKit/UITableViewHeaderFooterViewTests.swift +++ b/Tests/UIKit/UITableViewHeaderFooterViewTests.swift @@ -7,8 +7,8 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa - class UITableViewHeaderFooterViewTests: XCTestCase { func testPrepareForReuse() { diff --git a/Tests/UIKit/UITextFieldTests.swift b/Tests/UIKit/UITextFieldTests.swift index 8ce8d83..67434e0 100644 --- a/Tests/UIKit/UITextFieldTests.swift +++ b/Tests/UIKit/UITextFieldTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index d99f0e9..28a0836 100644 --- a/Tests/UIKit/UITextViewTests.swift +++ b/Tests/UIKit/UITextViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIViewControllerTests.swift b/Tests/UIKit/UIViewControllerTests.swift index 315b723..931992e 100644 --- a/Tests/UIKit/UIViewControllerTests.swift +++ b/Tests/UIKit/UIViewControllerTests.swift @@ -6,6 +6,7 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest diff --git a/Tests/UIKit/UIViewTests.swift b/Tests/UIKit/UIViewTests.swift index 5527177..6a5b9c6 100644 --- a/Tests/UIKit/UIViewTests.swift +++ b/Tests/UIKit/UIViewTests.swift @@ -6,6 +6,7 @@ // Copyright © 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit import XCTest From 42522ce2f5b7891451aa1143a6f4a9914d3b91ca Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:55:52 +0100 Subject: [PATCH 26/31] Fixed the failing `UIButton.rex_pressed` test. --- Rex.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index 4ca606f..33106c0 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 9A0501A01D86D76C006BBAE8 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */; }; 9A4375B41D86DBA000F04A59 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0501961D86D713006BBAE8 /* ReactiveSwift.framework */; }; 9A4375B51D86DBA000F04A59 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8003EAD1AFEC68A00D7D3C5 /* ReactiveCocoa.framework */; }; + 9A4375B61D86DD1D00F04A59 /* UIControl+EnableSendActionsForControlEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0DABA91CCC381F00B6CD2B /* UIControl+EnableSendActionsForControlEvents.swift */; }; 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; 9A5492141D33387D009A8D44 /* Deprecations+Removals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */; }; @@ -1015,6 +1016,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9A4375B61D86DD1D00F04A59 /* UIControl+EnableSendActionsForControlEvents.swift in Sources */, E6933BEB1CD9C363006F7CE7 /* UIProgressViewTests.swift in Sources */, D8715DE51C211643005F4191 /* UIViewTests.swift in Sources */, 7DCF5B371CC80E8E004AEE75 /* UICollectionReusableViewTests.swift in Sources */, From e95b087ee6054e0620ca2d6d812fe844457a23f0 Mon Sep 17 00:00:00 2001 From: Anders Date: Mon, 12 Sep 2016 13:58:44 +0100 Subject: [PATCH 27/31] Updated for Swift 3 GM. --- Source/Foundation/Association.swift | 6 +++--- Source/Property.swift | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 485ab32..77858c6 100644 --- a/Source/Foundation/Association.swift +++ b/Source/Foundation/Association.swift @@ -33,7 +33,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticString) -> Muta /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: @noescape () -> T) -> MutableProperty { +public func associatedProperty(_ host: AnyObject, keyPath: StaticString, placeholder: () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in host.setValue(newValue, forKeyPath: String(describing: keyPath)) } @@ -48,7 +48,7 @@ public func associatedProperty(_ host: AnyObject, keyPath: StaticS /// /// This can be used as an alternative to `DynamicProperty` for creating strongly typed /// bindings on Cocoa objects. -public func associatedProperty(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T, setter: @escaping (Host, T) -> (), setUp: @noescape (MutableProperty) -> () = { _ in }) -> MutableProperty { +public func associatedProperty(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T, setter: @escaping (Host, T) -> (), setUp: (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -67,7 +67,7 @@ public func associatedProperty(_ host: Host, key: UnsafePoin /// 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`. -public func associatedObject(_ host: Host, key: UnsafePointer<()>, initial: @noescape (Host) -> T) -> T { +public func associatedObject(_ host: Host, key: UnsafeRawPointer, initial: (Host) -> T) -> T { var value = objc_getAssociatedObject(host, key) as? T if value == nil { value = initial(host) diff --git a/Source/Property.swift b/Source/Property.swift index d383145..067c629 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -12,7 +12,7 @@ import enum Result.NoError extension PropertyProtocol where Value == Bool { /// The conjunction of `self` and `other`. - public func and(_ other: P) -> AndProperty { + public func and(_ other: P) -> AndProperty where P.Value == Bool { return AndProperty(terms: [Property(self), Property(other)]) } @@ -22,7 +22,7 @@ extension PropertyProtocol where Value == Bool { } /// The disjunction of `self` and `other`. - public func or(_ other: P) -> OrProperty { + public func or(_ other: P) -> OrProperty where P.Value == Bool { return OrProperty(terms: [Property(self), Property(other)]) } @@ -60,7 +60,7 @@ public class AndProperty: PropertyProtocol { } /// Creates a new property with an additional conjunctive term. - public func and

(_ other: P) -> AndProperty { + public func and

(_ other: P) -> AndProperty where P.Value == Bool { return AndProperty(terms: terms + [Property(other)]) } @@ -97,7 +97,7 @@ public class OrProperty: PropertyProtocol { } /// Creates a new property with an additional disjunctive term. - public func or

(_ other: P) -> OrProperty { + public func or

(_ other: P) -> OrProperty where P.Value == Bool { return OrProperty(terms: terms + [Property(other)]) } From cdac8dd4ebf58e87986b13092cf90676e107428a Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Thu, 15 Sep 2016 14:40:58 -0400 Subject: [PATCH 28/31] Leverage existing BindingTarget, try an alternate validation approach --- Source/Bindings/PropertyBinding.swift | 61 ++++++++++++++------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift index f86905a..cddf7bc 100644 --- a/Source/Bindings/PropertyBinding.swift +++ b/Source/Bindings/PropertyBinding.swift @@ -8,53 +8,54 @@ import Foundation import ReactiveSwift +import ReactiveCocoa import Result /// A `PropertyBinding` matches up a property with a function that validates incoming values to the property, and wraps the two into a new `MutablePropertyType` that can be treated as its own property, and bound to. -public final class PropertyBinding { - +public final class ValidatingBinding: BindingTarget { /// Validators are expected to return either `nil` if the input value is invalid, the passed-in value, or a corrected version of the passed-in value. The behavior is completely up to the implementor. - public typealias Validator = (Property.Value) -> Property.Value? + public typealias Validator = (Target.Value) -> Result - fileprivate let _property: Property + fileprivate let _target: Target fileprivate let _validator: Validator? - public init(property: Property, validator: Validator?) { - _property = property + public init(target: Target, validator: Validator?) { + _target = target _validator = validator } -} -extension PropertyBinding : MutablePropertyProtocol { /// The lifetime of `self`. The binding operators use this to determine when /// the binding should be teared down. public var lifetime: Lifetime { - return _property.lifetime + return _target.lifetime } - public var producer: SignalProducer { - return _property.producer - } - - public var signal: Signal { - return _property.signal - } - - public var value: Property.Value { - get { return _property.value } - set { _property.value = (_validator == nil) ? newValue : (_validator?(newValue) ?? _property.value) } + /// Stores the last validation error that was triggered upon consumption + public let validationError = MutableProperty(nil) + + /// Consume a value from the binding. + public func consume(_ value: Target.Value) { + guard let newValue = (_validator == nil) ? .success(value) : _validator?(value) else { + return + } + + switch newValue { + case .success(let validated): + _target.consume(validated) + case .failure(let error): + validationError.value = error + } } } -public extension TypedDynamicProperty { - public typealias Validator = (Value) -> Value? - public typealias Binding = PropertyBinding> - - /// Returns a binding for the specified `object` and `keyPath`, with an optional `validator` that returns a valid, replacement value, or nil if the value is invalid. - public class func bindingForObject(_ object: NSObject, withKeyPath keyPath: String, validator: Validator?) -> Binding { - return PropertyBinding(property: TypedDynamicProperty(object: object, keyPath: keyPath), validator: validator) +public extension NSObject { + /// Creates a binding for the given keyPath and supplied validator function + public func rex_binding(forKeyPath keyPath: String, validator: ValidatingBinding, ValidationError>.Validator?) -> ValidatingBinding, ValidationError> where Value: AnyObject, ValidationError: Error { + return ValidatingBinding(target: DynamicProperty(object: self, keyPath: keyPath), validator: validator) } - +} + +public extension BindingTarget where Value: AnyObject { /// Wraps this instance in a `PropertyBinding` object, using the specified validator function. /// /// Example: @@ -74,7 +75,7 @@ public extension TypedDynamicProperty { /// } /// } /// ``` - public func bindingWithValidator(_ validator: Validator?) -> Binding { - return PropertyBinding(property: self, validator: validator) + public func bindingWithValidator(_ validator: ValidatingBinding.Validator?) -> ValidatingBinding { + return ValidatingBinding(target: self, validator: validator) } } From 648bfc273e684b3a137d0b3eacf2a271e1647105 Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 16 Sep 2016 06:30:19 -0400 Subject: [PATCH 29/31] Add bridgeable value support --- Source/Bindings/PropertyBinding.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift index cddf7bc..d76cfdd 100644 --- a/Source/Bindings/PropertyBinding.swift +++ b/Source/Bindings/PropertyBinding.swift @@ -49,6 +49,11 @@ public final class ValidatingBinding(forKeyPath keyPath: String, validator: ValidatingBinding, ValidationError>.Validator?) -> ValidatingBinding, ValidationError> where Value: _ObjectiveCBridgeable, ValidationError: Error { + return ValidatingBinding(target: DynamicProperty(object: self, keyPath: keyPath), validator: validator) + } + /// Creates a binding for the given keyPath and supplied validator function public func rex_binding(forKeyPath keyPath: String, validator: ValidatingBinding, ValidationError>.Validator?) -> ValidatingBinding, ValidationError> where Value: AnyObject, ValidationError: Error { return ValidatingBinding(target: DynamicProperty(object: self, keyPath: keyPath), validator: validator) From 4a27fe54904417ce13b7b542fab026d2df522e2f Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 16 Sep 2016 09:37:20 -0400 Subject: [PATCH 30/31] Remove unnecessary TypedDynamicProperty, fix PropertyBinding --- Rex.xcodeproj/project.pbxproj | 10 ------ Source/Bindings/PropertyBinding.swift | 40 +++++++++++++++++----- Source/Bindings/TypedDynamicProperty.swift | 38 -------------------- 3 files changed, 32 insertions(+), 56 deletions(-) delete mode 100644 Source/Bindings/TypedDynamicProperty.swift diff --git a/Rex.xcodeproj/project.pbxproj b/Rex.xcodeproj/project.pbxproj index f16a963..85588a3 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -59,10 +59,6 @@ 83B8FB3D1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; 83B8FB3E1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; 83B8FB3F1C60F7100075D9AF /* PropertyBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */; }; - 83B8FB401C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; - 83B8FB411C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; - 83B8FB421C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; - 83B8FB431C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */; }; 83B8FB491C60F7220075D9AF /* NSButton+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB441C60F7220075D9AF /* NSButton+Bindable.swift */; }; 83B8FB4A1C60F7220075D9AF /* NSControl+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB451C60F7220075D9AF /* NSControl+Bindable.swift */; }; 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */; }; @@ -291,7 +287,6 @@ 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BindingValueConsumer.swift; path = Bindings/BindingValueConsumer.swift; sourceTree = ""; }; 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConsumerBinding.swift; path = Bindings/ConsumerBinding.swift; sourceTree = ""; }; 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PropertyBinding.swift; path = Bindings/PropertyBinding.swift; sourceTree = ""; }; - 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TypedDynamicProperty.swift; path = Bindings/TypedDynamicProperty.swift; sourceTree = ""; }; 83B8FB441C60F7220075D9AF /* NSButton+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSButton+Bindable.swift"; path = "Bindings/AppKit/NSButton+Bindable.swift"; sourceTree = ""; }; 83B8FB451C60F7220075D9AF /* NSControl+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSControl+Bindable.swift"; path = "Bindings/AppKit/NSControl+Bindable.swift"; sourceTree = ""; }; 83B8FB461C60F7220075D9AF /* NSPopUpButton+Bindable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSPopUpButton+Bindable.swift"; path = "Bindings/AppKit/NSPopUpButton+Bindable.swift"; sourceTree = ""; }; @@ -464,7 +459,6 @@ 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */, 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */, 83B8FB2E1C60F7100075D9AF /* PropertyBinding.swift */, - 83B8FB2F1C60F7100075D9AF /* TypedDynamicProperty.swift */, 83B8FB111C60F4C10075D9AF /* AppKit */, ); name = Bindings; @@ -969,7 +963,6 @@ 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.swift in Sources */, - 83B8FB401C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, 833859951C3E9B8B00EE372F /* NSControl.swift in Sources */, 83B8FB341C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, 83B8FB491C60F7220075D9AF /* NSButton+Bindable.swift in Sources */, @@ -1037,7 +1030,6 @@ 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, 83B8FB3D1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, - 83B8FB411C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, @@ -1090,7 +1082,6 @@ C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */, 9A5492151D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, 83B8FB3F1C60F7100075D9AF /* PropertyBinding.swift in Sources */, - 83B8FB431C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, ); @@ -1125,7 +1116,6 @@ D8715DBA1C2112D1005F4191 /* Action.swift in Sources */, 83B8FB3E1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D8715DC81C211553005F4191 /* UIButton.swift in Sources */, - 83B8FB421C60F7100075D9AF /* TypedDynamicProperty.swift in Sources */, D8715DC71C211553005F4191 /* UIBarItem.swift in Sources */, D8715DC11C2112D6005F4191 /* UserDefaults.swift in Sources */, 45CED4721D27C1EC00788BDC /* UIActivityIndicatorView.swift in Sources */, diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift index d76cfdd..829b44f 100644 --- a/Source/Bindings/PropertyBinding.swift +++ b/Source/Bindings/PropertyBinding.swift @@ -11,8 +11,8 @@ import ReactiveSwift import ReactiveCocoa import Result -/// A `PropertyBinding` matches up a property with a function that validates incoming values to the property, and wraps the two into a new `MutablePropertyType` that can be treated as its own property, and bound to. -public final class ValidatingBinding: BindingTarget { +/// A `PropertyBinding` matches up a property with a function that validates incoming values to the property, and wraps the two into a new type that can be bound to, and expose values from the underlying property. +public final class ValidatingBinding: BindingTarget where Target: PropertyProtocol { /// Validators are expected to return either `nil` if the input value is invalid, the passed-in value, or a corrected version of the passed-in value. The behavior is completely up to the implementor. public typealias Validator = (Target.Value) -> Result @@ -48,6 +48,29 @@ public final class ValidatingBinding { + return _target.signal + } + + /// The values producer of the property. + /// + /// It produces a signal that sends the property's current value, + /// followed by all changes over time. It completes when the property + /// has deinitialized, or has no further change. + public var producer: SignalProducer { + return _target.producer + } + + /// The current value of the property. + public var value: Target.Value { + return _target.value + } +} + public extension NSObject { /// Creates a binding for the given keyPath and supplied validator function public func rex_binding(forKeyPath keyPath: String, validator: ValidatingBinding, ValidationError>.Validator?) -> ValidatingBinding, ValidationError> where Value: _ObjectiveCBridgeable, ValidationError: Error { @@ -60,23 +83,24 @@ public extension NSObject { } } -public extension BindingTarget where Value: AnyObject { +public extension BindingTarget where Value: AnyObject, Self: PropertyProtocol { /// Wraps this instance in a `PropertyBinding` object, using the specified validator function. /// /// Example: /// /// ``` /// final class Volume { - /// dynamic var volume: Double // Between 0 and 1. Could be defined in an ObjC parent class. + /// let volume = MutableProperty(0) // Between 0 and 1 /// - /// var rex_volume: TypedDynamicProperty.Binding { + /// var volumeBinding: ValidatingBinding, NoError> { /// return associatedObject(self, key: &durationKey) { - /// TypedDynamicProperty(object: $0, keyPath: "volume").bindingWithValidator($0.validVolume) + /// $0.volume.bindingWithValidator($0.validVolume) /// } /// } /// - /// private func validVolume(newVolume: Double) -> Double? { - /// return max(0.0, min(1.0, newVolume)) + /// // Always returns success, merely clamping the value into place + /// private func validVolume(newVolume: Double?) -> Result { + /// return .success(max(0.0, min(1.0, newVolume ?? volume.value)) /// } /// } /// ``` diff --git a/Source/Bindings/TypedDynamicProperty.swift b/Source/Bindings/TypedDynamicProperty.swift deleted file mode 100644 index ab3938d..0000000 --- a/Source/Bindings/TypedDynamicProperty.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// TypedDynamicProperty.swift -// FuzzMeasure -// -// Created by Christopher Liscio on 1/7/16. -// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. -// - -import Foundation -import ReactiveSwift -import ReactiveCocoa -import Result - -/// Wraps a `DynamicProperty` instance in an effort to handle the casting that is often required when bridging from the KVO-powered class. -public final class TypedDynamicProperty : MutablePropertyProtocol { - /// The lifetime of `self`. The binding operators use this to determine when - /// the binding should be teared down. - public var lifetime: Lifetime { - return _property.lifetime - } - - fileprivate let _property: DynamicProperty - fileprivate init(_ property: DynamicProperty) { - _property = property - } - - public convenience init(object: NSObject, keyPath: String) { - self.init(DynamicProperty(object: object, keyPath: keyPath)) - } - - public var value: Value { - get { return _property.value! } - set { _property.value = newValue } - } - - public var producer: SignalProducer { return _property.producer.map { $0! } } - public var signal: Signal { return _property.signal.map { $0! } } -} From bbfbdfc86f5d4800cac98df3fa6257ae172204bf Mon Sep 17 00:00:00 2001 From: Christopher Liscio Date: Fri, 16 Sep 2016 11:06:53 -0400 Subject: [PATCH 31/31] Expose value typealias --- Source/Bindings/PropertyBinding.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift index 829b44f..30ab028 100644 --- a/Source/Bindings/PropertyBinding.swift +++ b/Source/Bindings/PropertyBinding.swift @@ -15,6 +15,7 @@ import Result public final class ValidatingBinding: BindingTarget where Target: PropertyProtocol { /// Validators are expected to return either `nil` if the input value is invalid, the passed-in value, or a corrected version of the passed-in value. The behavior is completely up to the implementor. public typealias Validator = (Target.Value) -> Result + public typealias Value = Target.Value fileprivate let _target: Target fileprivate let _validator: Validator?