diff --git a/.travis.yml b/.travis.yml index ca2289c..5ca52c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,6 @@ language: objective-c matrix: include: - - osx_image: xcode7.3 - env: PLATFORM=Mac - - osx_image: xcode7.3 - env: PLATFORM=iOS - - osx_image: xcode7.3 - env: PLATFORM=tvOS - - osx_image: xcode7.3 - env: PLATFORM=watchOS - - osx_image: xcode7.3 - env: PLATFORM=CocoaPods - osx_image: xcode8 env: PLATFORM=Mac TEST_ACTION="build-for-testing test-without-building" - osx_image: xcode8 diff --git a/Cartfile b/Cartfile index c46e125..38f3bb3 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.2 +github "ReactiveCocoa/ReactiveCocoa" "master" diff --git a/Cartfile.resolved b/Cartfile.resolved index 7267e9d..b035f1b 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,3 @@ -github "antitypical/Result" "2.1.3" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.2" +github "antitypical/Result" "3.0.0" +github "ReactiveCocoa/ReactiveSwift" "d4de1ff81c12124d148f726b8b44dd6887b41ae0" +github "ReactiveCocoa/ReactiveCocoa" "e7443157ad1257e93c660586c41e1da710547676" diff --git a/Deprecations+Removals.swift b/Deprecations+Removals.swift new file mode 100644 index 0000000..5b62761 --- /dev/null +++ b/Deprecations+Removals.swift @@ -0,0 +1,46 @@ +// MARK: Renamed APIs in Swift 3.0 +import ReactiveSwift +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 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 ed0a7f5..85588a3 100644 --- a/Rex.xcodeproj/project.pbxproj +++ b/Rex.xcodeproj/project.pbxproj @@ -39,6 +39,50 @@ 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; + 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 */; }; @@ -56,8 +100,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 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, ); }; }; + 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 */; }; @@ -67,7 +111,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 */; }; @@ -90,9 +133,8 @@ 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 */; }; 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 +143,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 */; }; @@ -134,10 +176,10 @@ 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 */; }; @@ -173,15 +215,17 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D8003EB21AFEC6A800D7D3C5 /* CopyFiles */ = { + D8003EB21AFEC6A800D7D3C5 /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 16; files = ( - D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in CopyFiles */, - D8003EB51AFEC6B000D7D3C5 /* Result.framework in CopyFiles */, + D8003EB41AFEC6B000D7D3C5 /* ReactiveCocoa.framework in Copy Files */, + 9A0501971D86D713006BBAE8 /* ReactiveSwift.framework in Copy Files */, + D8003EB51AFEC6B000D7D3C5 /* Result.framework in Copy Files */, ); + name = "Copy Files"; runOnlyForDeploymentPostprocessing = 0; }; D83457371AFEE4B80070616A /* CopyFiles */ = { @@ -191,6 +235,7 @@ dstSubfolderSpec = 16; files = ( D83457391AFEE4BE0070616A /* ReactiveCocoa.framework in CopyFiles */, + 9A0501931D86D6D9006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D834573A1AFEE4BE0070616A /* Result.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -201,6 +246,7 @@ dstPath = ""; dstSubfolderSpec = 16; files = ( + 9A05019D1D86D751006BBAE8 /* ReactiveSwift.framework in CopyFiles */, D8715DE91C211739005F4191 /* ReactiveCocoa.framework in CopyFiles */, D8715DEA1C211739005F4191 /* Result.framework in CopyFiles */, ); @@ -212,8 +258,8 @@ 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 = ""; }; - 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; }; + 5173EBC51B625A2600C9B48E /* UIBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarItem.swift; sourceTree = ""; }; + 5173EBC71B625A6800C9B48E /* UIBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = ""; }; 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 = ""; }; @@ -233,11 +279,32 @@ 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; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 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; }; C72CF3E41CBF188A00E19897 /* RACSignal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RACSignal.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - C7932E811C4B3EDB00086F3C /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UITextField.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - C7932E851C4B420A00086F3C /* UITextFieldTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UITextFieldTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 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 = ""; }; C7945F101CC192E800DC9E37 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C7945F121CC1DFB400DC9E37 /* UIViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UIViewControllerTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C7DCE2B21CB3C872001217D8 /* UITextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UITextView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -255,7 +322,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; }; @@ -273,8 +340,8 @@ 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; }; - 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 = ""; }; @@ -286,8 +353,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; }; @@ -296,6 +364,7 @@ buildActionMask = 2147483647; files = ( D8003EC51AFED36F00D7D3C5 /* ReactiveCocoa.framework in Frameworks */, + 9A0501981D86D71D006BBAE8 /* ReactiveSwift.framework in Frameworks */, D8003EC61AFED36F00D7D3C5 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -304,8 +373,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; }; @@ -314,6 +384,7 @@ buildActionMask = 2147483647; files = ( D834573C1AFEE57E0070616A /* ReactiveCocoa.framework in Frameworks */, + 9A0501941D86D6F0006BBAE8 /* ReactiveSwift.framework in Frameworks */, D834573D1AFEE57E0070616A /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -322,8 +393,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; }; @@ -331,8 +403,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; }; @@ -342,6 +415,7 @@ files = ( D8715DE61C21170C005F4191 /* ReactiveCocoa.framework in Frameworks */, D8715DE71C21170C005F4191 /* Result.framework in Frameworks */, + 9A05019B1D86D737006BBAE8 /* ReactiveSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -351,7 +425,12 @@ 4238D5941B4D593E008534C0 /* AppKit */ = { isa = PBXGroup; children = ( + 83B8FB561C6112EA0075D9AF /* NSButton.swift */, + 83B8FB4F1C6102F10075D9AF /* NSSlider.swift */, + 83B8FB511C6102F10075D9AF /* NSViewController.swift */, 4238D5951B4D5950008534C0 /* NSTextField.swift */, + 833859941C3E9B8B00EE372F /* NSControl.swift */, + 833859961C3E9FEC00EE372F /* NSPopUpButton.swift */, ); name = AppKit; sourceTree = ""; @@ -364,6 +443,39 @@ path = Helpers; sourceTree = ""; }; + 833859981C3EA14400EE372F /* AppKit */ = { + isa = PBXGroup; + children = ( + 833859991C3EA15A00EE372F /* NSControlTests.swift */, + 8338599B1C3EA23B00EE372F /* NSTextFieldTests.swift */, + ); + name = AppKit; + sourceTree = ""; + }; + 83B8FB061C60F49D0075D9AF /* Bindings */ = { + isa = PBXGroup; + children = ( + 83B8FB2B1C60F7100075D9AF /* BindingValue.swift */, + 83B8FB2C1C60F7100075D9AF /* BindingValueConsumer.swift */, + 83B8FB2D1C60F7100075D9AF /* ConsumerBinding.swift */, + 83B8FB2E1C60F7100075D9AF /* PropertyBinding.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 = ( @@ -371,7 +483,10 @@ D8003E9D1AFEC3D400D7D3C5 /* Tests */, D8003EAA1AFEC57200D7D3C5 /* Binaries */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 0; }; D8003E901AFEC3D400D7D3C5 /* Source */ = { isa = PBXGroup; @@ -382,10 +497,12 @@ D8003EB81AFEC7A900D7D3C5 /* SignalProducer.swift */, C72CF3E41CBF188A00E19897 /* RACSignal.swift */, 7DBD48F21CC8141D0077AD4F /* Reusable.swift */, + 83B8FB061C60F49D0075D9AF /* Bindings */, 4238D5941B4D593E008534C0 /* AppKit */, D8F097391B17F2BF002E15BA /* Foundation */, D86FFBD31B34B0E2001A89B3 /* UIKit */, D8003E911AFEC3D400D7D3C5 /* Supporting Files */, + 9A5492111D33387D009A8D44 /* Deprecations+Removals.swift */, ); path = Source; sourceTree = ""; @@ -403,6 +520,7 @@ isa = PBXGroup; children = ( CC02C1881CCA704C0025CC04 /* ActionTests.swift */, + 833859981C3EA14400EE372F /* AppKit */, D8A454081BD2772700C9E790 /* PropertyTests.swift */, D8003EBE1AFED2F800D7D3C5 /* SignalTests.swift */, D8003EC01AFED30100D7D3C5 /* SignalProducerTests.swift */, @@ -426,7 +544,7 @@ isa = PBXGroup; children = ( D8003EC71AFEE39000D7D3C5 /* iOS */, - D8003EAB1AFEC67700D7D3C5 /* Mac */, + D8003EAB1AFEC67700D7D3C5 /* macOS */, D8715DB81C21124B005F4191 /* tvOS */, D8715D9C1C210FA1005F4191 /* watchOS */, ); @@ -434,14 +552,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 = ""; }; @@ -450,6 +570,7 @@ children = ( D86E77AB1AFEE646004BF23D /* Rex.framework */, D86E77AC1AFEE646004BF23D /* RexTests-iOS.xctest */, + 9A0501921D86D6D9006BBAE8 /* ReactiveSwift.framework */, D8003EC91AFEE3ED00D7D3C5 /* ReactiveCocoa.framework */, D8003ECA1AFEE3ED00D7D3C5 /* Result.framework */, ); @@ -486,6 +607,7 @@ children = ( D8715D941C210F97005F4191 /* Rex.framework */, D8715DA71C2110DA005F4191 /* ReactiveCocoa.framework */, + 9A05019F1D86D765006BBAE8 /* ReactiveSwift.framework */, D8715DA81C2110DA005F4191 /* Result.framework */, ); name = watchOS; @@ -496,6 +618,7 @@ children = ( D8715DB01C21123E005F4191 /* Rex.framework */, D8715DD11C21160A005F4191 /* RexTests-tvOS.xctest */, + 9A05019A1D86D737006BBAE8 /* ReactiveSwift.framework */, D8715DC21C211310005F4191 /* ReactiveCocoa.framework */, D8715DC31C211310005F4191 /* Result.framework */, ); @@ -530,9 +653,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 = ""; @@ -583,9 +706,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 */, @@ -596,28 +719,28 @@ ); 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 */, D8003E971AFEC3D400D7D3C5 /* Resources */, - D8003EB21AFEC6A800D7D3C5 /* CopyFiles */, + D8003EB21AFEC6A800D7D3C5 /* Copy Files */, ); buildRules = ( ); 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 */ = { @@ -719,7 +842,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Neil Pankey"; TargetAttributes = { D8003E8D1AFEC3D400D7D3C5 = { @@ -764,8 +887,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 */, @@ -834,14 +957,29 @@ files = ( C72CF3E51CBF188A00E19897 /* RACSignal.swift in Sources */, D8A454061BD26A1A00C9E790 /* Property.swift in Sources */, + 83B8FB4A1C60F7220075D9AF /* NSControl+Bindable.swift in Sources */, + 83B8FB301C60F7100075D9AF /* BindingValue.swift in Sources */, D86FFBDA1B34B3F0001A89B3 /* Action.swift in Sources */, + 9A5492121D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, D86FFBD11B34AD6F001A89B3 /* Association.swift in Sources */, D8003EB91AFEC7A900D7D3C5 /* SignalProducer.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 */, - D8F0973B1B17F2F7002E15BA /* NSData.swift in Sources */, + 83B8FB4B1C60F7220075D9AF /* NSPopUpButton+Bindable.swift in Sources */, + 83B8FB551C6102F10075D9AF /* NSViewController.swift in Sources */, + D8F0973B1B17F2F7002E15BA /* Data.swift in Sources */, + 83B8FB531C6102F10075D9AF /* NSSlider.swift in Sources */, + 83B8FB4C1C60F7220075D9AF /* NSSlider+Bindable.swift in Sources */, 4238D5961B4D5950008534C0 /* NSTextField.swift in Sources */, - D8F0973D1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + 83B8FB3C1C60F7100075D9AF /* PropertyBinding.swift in Sources */, + 83B8FB4D1C60F7220075D9AF /* NSTextField+Bindable.swift in Sources */, + D8F0973D1B17F30D002E15BA /* UserDefaults.swift in Sources */, + 833859971C3E9FEC00EE372F /* NSPopUpButton.swift in Sources */, + 83B8FB571C6112EA0075D9AF /* NSButton.swift in Sources */, + 83B8FB381C60F7100075D9AF /* ConsumerBinding.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -850,10 +988,12 @@ buildActionMask = 2147483647; files = ( CC02C18A1CCA704E0025CC04 /* ActionTests.swift in Sources */, + 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; }; @@ -862,6 +1002,7 @@ buildActionMask = 2147483647; files = ( 9DA915A41CA6301C003723B9 /* UIDatePicker.swift in Sources */, + 83B8FB311C60F7100075D9AF /* BindingValue.swift in Sources */, D86FFBD81B34B242001A89B3 /* UILabel.swift in Sources */, 7DCF5B331CC80D77004AEE75 /* UICollectionReusableView.swift in Sources */, D86FFBDB1B34B3F0001A89B3 /* Action.swift in Sources */, @@ -870,8 +1011,10 @@ 7DC325761CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, 7DC325741CC6FCF100746D88 /* UITableViewCell.swift in Sources */, 5B7F81E31D0842AD0014B06D /* UISegmentedControl.swift in Sources */, + 83B8FB351C60F7100075D9AF /* BindingValueConsumer.swift in Sources */, 8289A2E31BD7EF740097FB60 /* UIImageView.swift in Sources */, - D8F0973E1B17F30D002E15BA /* NSUserDefaults.swift in Sources */, + D8F0973E1B17F30D002E15BA /* UserDefaults.swift in Sources */, + 83B8FB391C60F7100075D9AF /* ConsumerBinding.swift in Sources */, D834572D1AFEE45B0070616A /* Signal.swift in Sources */, D8E4A6211B7BBB2100EAD8A8 /* UIBarItem.swift in Sources */, 7D2AA99B1CB6EFEB008AB5C9 /* UISwitch.swift in Sources */, @@ -885,9 +1028,11 @@ D834572E1AFEE45B0070616A /* SignalProducer.swift in Sources */, C7DCE2B41CB3C89A001217D8 /* UITextView.swift in Sources */, 8289A2E51BD7F6DD0097FB60 /* UIView.swift in Sources */, + 83B8FB3D1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D86FFBD61B34B116001A89B3 /* UIControl.swift in Sources */, - D8F0973F1B17F31E002E15BA /* NSData.swift in Sources */, + D8F0973F1B17F31E002E15BA /* Data.swift in Sources */, D86FFBDD1B34B691001A89B3 /* UIButton.swift in Sources */, + 9A5492131D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, 45CED4711D27C1EB00788BDC /* UIActivityIndicatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -927,14 +1072,18 @@ 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 */, C72CF3E81CBF188A00E19897 /* RACSignal.swift in Sources */, - D8715DA41C21107F005F4191 /* NSData.swift in Sources */, + 9A5492151D33387D009A8D44 /* Deprecations+Removals.swift in Sources */, + 83B8FB3F1C60F7100075D9AF /* PropertyBinding.swift in Sources */, D8715D9D1C210FF9005F4191 /* Action.swift in Sources */, - D8715DA61C21107F005F4191 /* NSUserDefaults.swift in Sources */, + D8715DA61C21107F005F4191 /* UserDefaults.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -942,14 +1091,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83B8FB321C60F7100075D9AF /* BindingValue.swift in Sources */, 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 */, + 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 */, 7DC325771CC6FCF100746D88 /* UITableViewHeaderFooterView.swift in Sources */, D8715DC01C2112D6005F4191 /* NSObject.swift in Sources */, @@ -958,12 +1111,13 @@ 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 */, + 83B8FB3E1C60F7100075D9AF /* PropertyBinding.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; @@ -972,6 +1126,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 */, @@ -997,7 +1152,7 @@ /* Begin PBXTargetDependency section */ D8003E9C1AFEC3D400D7D3C5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D8003E8D1AFEC3D400D7D3C5 /* Rex-Mac */; + target = D8003E8D1AFEC3D400D7D3C5 /* Rex-macOS */; targetProxy = D8003E9B1AFEC3D400D7D3C5 /* PBXContainerItemProxy */; }; D83457211AFEE44E0070616A /* PBXTargetDependency */ = { @@ -1050,12 +1205,13 @@ 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; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1091,10 +1247,11 @@ 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; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1118,11 +1275,11 @@ 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 = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1144,10 +1301,11 @@ 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"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1184,6 +1342,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; }; @@ -1240,6 +1399,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1283,6 +1443,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "me.neilpa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1330,6 +1491,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; @@ -1379,6 +1541,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; @@ -1413,6 +1576,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; }; @@ -1430,7 +1594,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 */, @@ -1439,7 +1603,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 cc58cdc..475a6e0 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-Mac.xcscheme @@ -1,6 +1,6 @@ @@ -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/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme index ae01a48..25982f1 100644 --- a/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme +++ b/Rex.xcodeproj/xcshareddata/xcschemes/Rex-iOS.xcscheme @@ -1,6 +1,6 @@ { - return self.executing.signal + return self.isExecuting.signal .filterMap { $0 ? () : nil } } @@ -25,7 +26,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 @@ -43,11 +44,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/AppKit/NSButton.swift b/Source/AppKit/NSButton.swift new file mode 100644 index 0000000..24a71b4 --- /dev/null +++ b/Source/AppKit/NSButton.swift @@ -0,0 +1,28 @@ +// +// NSButton+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2016-01-11. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveSwift +import ReactiveCocoa +import Result + +extension NSButton { + public var rex_stateAction: Action { + return associatedObject(self, key: &stateActionKey) { _ in Action { SignalProducer(value: $0.state) } } + } + + public 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 diff --git a/Source/AppKit/NSControl.swift b/Source/AppKit/NSControl.swift new file mode 100644 index 0000000..1a42e76 --- /dev/null +++ b/Source/AppKit/NSControl.swift @@ -0,0 +1,54 @@ +// +// NSControl.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import AppKit +import ReactiveSwift +import ReactiveCocoa +import Result + +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.isEnabled }, setter: { $0.isEnabled = $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 diff --git a/Source/AppKit/NSPopUpButton.swift b/Source/AppKit/NSPopUpButton.swift new file mode 100644 index 0000000..6180deb --- /dev/null +++ b/Source/AppKit/NSPopUpButton.swift @@ -0,0 +1,63 @@ +// +// NSPopUpButton.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +import Foundation +import AppKit +import ReactiveSwift +import ReactiveCocoa +import Result + +extension NSPopUpButton { + public var rex_indexOfSelectedItem: MutableProperty { + return associatedProperty(self, key: &indexOfSelectedItemKey, initial: { $0.indexOfSelectedItem }, setter: { $0.selectItem(at: $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 + }) + } + + public var rex_selectedIndexAction: Action { + return associatedObject(self, key: &selectedIndexActionKey) { _ in Action { SignalProducer(value: $0.indexOfSelectedItem) } } + } + + 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 + } + + public var rex_selectedTagAction: Action { + return associatedObject(self, key: &selectedTagActionKey) { _ in Action { SignalProducer(value: $0.selectedTag()) } } + } + + public 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 +private var selectedIndexKey: UInt8 = 0 +private var selectedIndexActionKey: UInt8 = 0 +private var selectedTagKey: UInt8 = 0 +private var selectedTagActionKey: UInt8 = 0 diff --git a/Source/AppKit/NSSlider.swift b/Source/AppKit/NSSlider.swift new file mode 100644 index 0000000..24da290 --- /dev/null +++ b/Source/AppKit/NSSlider.swift @@ -0,0 +1,28 @@ +// +// NSSlider+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/6/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveSwift +import ReactiveCocoa +import Result + +extension NSSlider { + public var rex_doubleValueAction: Action { + return associatedObject(self, key: &doubleValueActionKey) { _ in Action { SignalProducer(value: $0.doubleValue) } } + } + + public 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 diff --git a/Source/AppKit/NSTextField.swift b/Source/AppKit/NSTextField.swift index cf14eee..7e8b23f 100644 --- a/Source/AppKit/NSTextField.swift +++ b/Source/AppKit/NSTextField.swift @@ -6,17 +6,37 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import AppKit import enum Result.NoError +import Result extension NSTextField { - /// Sends the field's string value whenever it changes. - public var rex_textSignal: SignalProducer { - return NSNotificationCenter.defaultCenter() - .rac_notifications(NSControlTextDidChangeNotification, object: self) + /// Sends the text field's string value as it changes. Equivalent to the "continuous" + /// control setting in IB + public var rex_continuousStringValues: SignalProducer { + return NotificationCenter.default + .rac_notifications(forName: NSNotification.Name.NSControlTextDidChange, 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 NotificationCenter.default + .rac_notifications(forName: NSNotification.Name.NSControlTextDidEndEditing, 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 diff --git a/Source/AppKit/NSViewController.swift b/Source/AppKit/NSViewController.swift new file mode 100644 index 0000000..52a288d --- /dev/null +++ b/Source/AppKit/NSViewController.swift @@ -0,0 +1,38 @@ +// +// NSViewController+ReactiveExtensions.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/30/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveSwift +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_signal(for: #selector(NSViewController.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 diff --git a/Source/Bindings/AppKit/NSButton+Bindable.swift b/Source/Bindings/AppKit/NSButton+Bindable.swift new file mode 100644 index 0000000..58a81f4 --- /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 { + public 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 diff --git a/Source/Bindings/AppKit/NSControl+Bindable.swift b/Source/Bindings/AppKit/NSControl+Bindable.swift new file mode 100644 index 0000000..a7a3af8 --- /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 { + public var rex_enabledBinding: ConsumerBinding { + return associatedObject(self, key: &enabledBindingKey) { control in + ConsumerBinding() { value in + switch value { + case let .value(v): + control.isEnabled = v + default: + // If the enabled states are mixed, then we assume it's false + control.isEnabled = false + } + } + } + } +} + +private var enabledBindingKey: UInt8 = 0 diff --git a/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift new file mode 100644 index 0000000..d02e989 --- /dev/null +++ b/Source/Bindings/AppKit/NSPopUpButton+Bindable.swift @@ -0,0 +1,99 @@ +// +// 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 { + + public 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) + } + } + } + } + + public var rex_selectedIndexBinding: ConsumerBinding { + return associatedObject(self, key: &selectedIndexBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .value(v): + button.selectItem(at: v) + (button.cell as? NSPopUpButtonCell)?.menuItem = button.itemArray[v] + button.synchronizeTitleAndSelectedItem() + default: + button.displayPlaceholder(value) + } + } + } + } + + public var rex_selectedTagBinding: ConsumerBinding { + return associatedObject(self, key: &selectedTagBindingKey) { button in + ConsumerBinding() { value in + switch value { + case let .value(v): + button.selectItem(withTag: v) + default: + button.displayPlaceholder(value) + } + } + } + } + + public 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` + fileprivate func displayPlaceholder(_ placeholder: BindingValue) { + precondition(placeholder.isPlaceholder, "displayPlaceholder must be called with a placeholder value.") + + let font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: self.controlSize)) + + let placeholder = placeholder.formatString({ _ in return "" }) + + let disabledColor: NSColor + if #available(OSX 10.10, *) { + disabledColor = NSColor.tertiaryLabelColor + } else { + disabledColor = NSColor.black.withAlphaComponent(0.5) + } + + let attrs: [String : Any] = [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 diff --git a/Source/Bindings/AppKit/NSSlider+Bindable.swift b/Source/Bindings/AppKit/NSSlider+Bindable.swift new file mode 100644 index 0000000..5c69f60 --- /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 { + public 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 diff --git a/Source/Bindings/AppKit/NSTextField+Bindable.swift b/Source/Bindings/AppKit/NSTextField+Bindable.swift new file mode 100644 index 0000000..c55795d --- /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 { + public 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 diff --git a/Source/Bindings/BindingValue.swift b/Source/Bindings/BindingValue.swift new file mode 100644 index 0000000..8d6d4af --- /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.suffix(from: 1).reduce(true) { $0 && ($1 == firstValue) } + if allEqual { + self = BindingValue.value(firstValue) + } + else { + self = BindingValue.multipleValues + } + } + } +} diff --git a/Source/Bindings/BindingValueConsumer.swift b/Source/Bindings/BindingValueConsumer.swift new file mode 100644 index 0000000..70ae342 --- /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 ReactiveSwift + +/// A consumer of `BindingValue`s +public protocol BindingValueConsumer { + associatedtype Value + associatedtype Error: Swift.Error + + /// Bind the object to a producer of `BindingValue`s + func bind(producer: SignalProducer, Error>) + func bind(signal: Signal, Error>) +} + +public func <~(control: Control, producer: SignalProducer) where Control.Value == Value, Control.Error == Error { + control.bind(producer: producer.map { BindingValue.value($0) }) +} + +public func <~(control: Control, signal: Signal) where Control.Value == Value, Control.Error == Error { + control.bind(signal: signal.map { BindingValue.value($0) }) +} + +/// Connect a producer of arrays of values (i.e. a selection, or many-to-one binding) to a `BindingValueConsumer` +public func <~(control: Control, producer: SignalProducer<[Value], Error>) where Control.Value == Value, Control.Error == Error { + control.bind(producer: 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`. +public func <~(control: Control, producer: SignalProducer, Error>) where Control.Value == Value, Control.Error == Error { + control.bind(producer: producer) +} diff --git a/Source/Bindings/ConsumerBinding.swift b/Source/Bindings/ConsumerBinding.swift new file mode 100644 index 0000000..24f9fed --- /dev/null +++ b/Source/Bindings/ConsumerBinding.swift @@ -0,0 +1,42 @@ +// +// ConsumerBinding.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 2016-01-09. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +import Foundation +import ReactiveSwift +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. +/// +/// 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 { + fileprivate let _handler: (BindingValue) -> Void + fileprivate 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: @escaping (BindingValue) -> Void) { + _handler = handler + _disposable = CompositeDisposable() + } +} + +extension ConsumerBinding : BindingValueConsumer { + /// Binds the specified producer to this instance + public func bind(producer: SignalProducer, NoError>) { + _disposable += producer + .observe(on: UIScheduler()) // Assert the handlers happen on the UI thread + .startWithNext(_handler) + } + + /// Binds the specified signal to this instance + public func bind(signal: Signal, NoError>) { + _disposable += signal + .observe(on: UIScheduler()) // Assert the handlers happen on the UI thread + .observeNext(_handler) + } +} diff --git a/Source/Bindings/PropertyBinding.swift b/Source/Bindings/PropertyBinding.swift new file mode 100644 index 0000000..30ab028 --- /dev/null +++ b/Source/Bindings/PropertyBinding.swift @@ -0,0 +1,111 @@ +// +// PropertyBinding.swift +// FuzzMeasure +// +// Created by Christopher Liscio on 1/6/16. +// Copyright © 2016 SuperMegaUltraGroovy, Inc. All rights reserved. +// + +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 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 + public typealias Value = Target.Value + + fileprivate let _target: Target + fileprivate let _validator: Validator? + + public init(target: Target, validator: Validator?) { + _target = target + _validator = validator + } + + /// The lifetime of `self`. The binding operators use this to determine when + /// the binding should be teared down. + public var lifetime: Lifetime { + return _target.lifetime + } + + /// 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 + } + } +} + +extension ValidatingBinding: PropertyProtocol { + /// A signal that will send the property's changes over time. It + /// completes when the property has deinitialized, or has no further + /// change. + public var signal: Signal { + 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 { + 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) + } +} + +public extension BindingTarget where Value: AnyObject, Self: PropertyProtocol { + /// Wraps this instance in a `PropertyBinding` object, using the specified validator function. + /// + /// Example: + /// + /// ``` + /// final class Volume { + /// let volume = MutableProperty(0) // Between 0 and 1 + /// + /// var volumeBinding: ValidatingBinding, NoError> { + /// return associatedObject(self, key: &durationKey) { + /// $0.volume.bindingWithValidator($0.validVolume) + /// } + /// } + /// + /// // 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)) + /// } + /// } + /// ``` + public func bindingWithValidator(_ validator: ValidatingBinding.Validator?) -> ValidatingBinding { + return ValidatingBinding(target: self, validator: validator) + } +} diff --git a/Source/Foundation/Association.swift b/Source/Foundation/Association.swift index 976b6c9..77858c6 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 @@ -15,13 +16,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(describing: keyPath)) as? String ?? "" } let setter: (AnyObject, String) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(describing: keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: initial, setter: setter) } @@ -33,13 +33,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: () -> T) -> MutableProperty { let setter: (AnyObject, T) -> () = { host, newValue in - host.setValue(newValue, forKeyPath: keyPath.stringValue) + host.setValue(newValue, forKeyPath: String(describing: keyPath)) } return associatedProperty(host, key: keyPath.utf8Start, initial: { host in - host.valueForKeyPath(keyPath.stringValue) as? T ?? placeholder() + host.value(forKeyPath: String(describing: keyPath)) as? T ?? placeholder() }, setter: setter) } @@ -49,8 +48,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: UnsafeRawPointer, initial: (Host) -> T, setter: @escaping (Host, T) -> (), setUp: (MutableProperty) -> () = { _ in }) -> MutableProperty { return associatedObject(host, key: key) { host in let property = MutableProperty(initial(host)) @@ -69,7 +67,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: UnsafeRawPointer, initial: (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..4f77d0b --- /dev/null +++ b/Source/Foundation/Data.swift @@ -0,0 +1,31 @@ +// +// Data.swift +// Rex +// +// Created by Ilya Laryionau on 10/05/15. +// Copyright (c) 2015 Neil Pankey. All rights reserved. +// + +import ReactiveSwift + +extension Data { + /// Read the data at the URL, sending the result or an error. + 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) + observer.sendNext(data) + observer.sendCompleted() + } catch { + observer.sendFailed(error as NSError) + } + } + } +} + +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/Foundation/NSObject.swift b/Source/Foundation/NSObject.swift index c839ef1..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 @@ -15,16 +16,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_producer(forKeyPath 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 58% rename from Source/Foundation/NSUserDefaults.swift rename to Source/Foundation/UserDefaults.swift index 702dd93..061e0e2 100644 --- a/Source/Foundation/NSUserDefaults.swift +++ b/Source/Foundation/UserDefaults.swift @@ -6,30 +6,30 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift 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_value(forKey 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) + 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/Property.swift b/Source/Property.swift index 230d0ab..067c629 100644 --- a/Source/Property.swift +++ b/Source/Property.swift @@ -6,44 +6,40 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift 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 { - return AndProperty(terms: [AnyProperty(self), AnyProperty(other)]) + public func and(_ other: P) -> AndProperty where P.Value == Bool { + return AndProperty(terms: [Property(self), Property(other)]) } /// The conjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - 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`. - @warn_unused_result(message="Did you forget to use the property?") - public func or(other: P) -> OrProperty { - return OrProperty(terms: [AnyProperty(self), AnyProperty(other)]) + public func or(_ other: P) -> OrProperty where P.Value == Bool { + return OrProperty(terms: [Property(self), Property(other)]) } /// The disjunction of `self` and `other`. - @warn_unused_result(message="Did you forget to use the property?") - 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`. - @warn_unused_result(message="Did you forget to use the property?") 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: PropertyType { - public let terms: [AnyProperty] +public class AndProperty: PropertyProtocol { + public let terms: [Property] public var value: Bool { return terms.reduce(true) { $0 && $1.value } @@ -51,38 +47,36 @@ 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 { - return AndProperty(terms: terms + [AnyProperty(other)]) + public func and

(_ other: P) -> AndProperty where P.Value == Bool { + return AndProperty(terms: terms + [Property(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: Property) -> AndProperty { return AndProperty(terms: terms + [other]) } - private init(terms: [AnyProperty]) { + fileprivate init(terms: [Property]) { self.terms = terms } } /// Specialized `PropertyType` for the disjunction of a set of boolean properties. -public struct OrProperty: PropertyType { - public let terms: [AnyProperty] +public class OrProperty: PropertyProtocol { + public let terms: [Property] public var value: Bool { return terms.reduce(false) { $0 || $1.value } @@ -90,38 +84,36 @@ 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 { - return OrProperty(terms: terms + [AnyProperty(other)]) + public func or

(_ other: P) -> OrProperty where P.Value == Bool { + return OrProperty(terms: terms + [Property(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: Property) -> OrProperty { return OrProperty(terms: terms + [other]) } - private init(terms: [AnyProperty]) { + fileprivate init(terms: [Property]) { self.terms = terms } } /// Specialized `PropertyType` for the negation of a boolean property. -public struct NotProperty: PropertyType { - private let source: AnyProperty +public class NotProperty: PropertyProtocol { + private let source: Property private let invert: Bool public var value: Bool { @@ -137,12 +129,11 @@ 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) } - private init(source: AnyProperty, invert: Bool) { + fileprivate init(source: Property, invert: Bool) { self.source = source self.invert = invert } diff --git a/Source/RACSignal.swift b/Source/RACSignal.swift index 8d01fa5..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 @@ -17,8 +18,7 @@ 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 { + public func rex_toSignal() -> Signal { return Signal { observer in return self.toSignalProducer().start(observer) } @@ -27,7 +27,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/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 4bc4837..2e796ea 100644 --- a/Source/Signal.swift +++ b/Source/Signal.swift @@ -6,27 +6,27 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift 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: @escaping (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 +35,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 +59,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 +83,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 mute(for 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 +99,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..46f08dc 100644 --- a/Source/SignalProducer.swift +++ b/Source/SignalProducer.swift @@ -6,16 +6,16 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift 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: @escaping (Value) -> Key) -> SignalProducer<(Key, SignalProducer), Error> { return SignalProducer<(Key, SignalProducer), Error> { observer, disposable in var groups: [Key: Signal.Observer] = [:] @@ -24,13 +24,13 @@ extension SignalProducerType { 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 +40,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 +58,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: @escaping (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 +74,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 +85,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 { - return lift { $0.muteFor(interval, clock: clock) } + public func mute(for interval: TimeInterval, clock: DateSchedulerProtocol) -> SignalProducer { + return lift { $0.mute(for: 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 +109,148 @@ 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 { + fileprivate 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 terminationEvent: Event? = state.modify { state in + replayValues = state.values + if replayValues.isEmpty { + token = state.observers?.insert(observer) + } else { + replayToken = state.replayBuffers.insert(replayBuffer) + } + return state.terminationEvent + } + + while !replayValues.isEmpty { + replayValues.forEach(observer.sendNext) + + terminationEvent = 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) + } + return state.terminationEvent + } + } + + if let terminationEvent = 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 observers: Bag.Observer>? = state.modify { state in + defer { + if let value = event.value { + state.add(value, upTo: capacity) + } else { + // Disconnect all observers and prevent future + // attachments. + state.terminationEvent = event + state.observers = nil + } + } + return state.observers + } + + observers?.forEach { $0.action(event) } + } + + return (producer, bufferingObserver) + } +} + +/// A uniquely identifying token for Observers that are replaying values in +/// BufferState. +fileprivate final class ReplayBuffer { + var values: [Value] = [] +} + +fileprivate struct BufferState { + /// All values in the buffer. + var values: [V] = [] + + /// 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: V, 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: &animatingKey, initial: { $0.isAnimating() }, setter: { host, animating in + return associatedProperty(self, key: &animatingKey, initial: { $0.isAnimating }, setter: { host, animating in if animating { host.startAnimating() } else { @@ -23,7 +24,6 @@ extension UIActivityIndicatorView { } }) } - } private var animatingKey: UInt8 = 0 diff --git a/Source/UIKit/UIBarButtonItem.swift b/Source/UIKit/UIBarButtonItem.swift index 25a05c5..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 @@ -25,7 +26,7 @@ extension UIBarButtonItem { host?.action = CocoaAction.selector } - host.rex_enabled <~ property.producer.flatMap(.Latest) { $0.rex_enabledProducer } + host.rex_enabled <~ property.producer.flatMap(.latest) { $0.rex_enabledProducer } return property } diff --git a/Source/UIKit/UIBarItem.swift b/Source/UIKit/UIBarItem.swift index 999bd94..710ee88 100644 --- a/Source/UIKit/UIBarItem.swift +++ b/Source/UIKit/UIBarItem.swift @@ -6,13 +6,14 @@ // Copyright (c) 2015 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa import UIKit extension UIBarItem { /// Wraps a UIBarItem'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 }) } } diff --git a/Source/UIKit/UIButton.swift b/Source/UIKit/UIButton.swift index ea49aef..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 @@ -22,11 +23,11 @@ extension UIButton { property.producer .combinePrevious(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 +36,7 @@ extension UIButton { /// Wraps a button's `title` text in a bindable property. Note that this only applies /// to `UIControlState.Normal`. public var rex_title: MutableProperty { - 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..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 @@ -14,9 +15,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 +27,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: @escaping (Host) -> T, setter: @escaping (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 +39,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..3b14724 100644 --- a/Source/UIKit/UIDatePicker.swift +++ b/Source/UIKit/UIDatePicker.swift @@ -6,13 +6,14 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift import ReactiveCocoa 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/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 465c76e..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 @@ -13,7 +14,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..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 @@ -13,6 +14,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..70c23a2 100644 --- a/Source/UIKit/UITextField.swift +++ b/Source/UIKit/UITextField.swift @@ -6,22 +6,25 @@ // Copyright © 2016 Neil Pankey. All rights reserved. // +import ReactiveSwift +import ReactiveSwift import ReactiveCocoa import UIKit +import Result 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) #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/Source/UIKit/UITextView.swift b/Source/UIKit/UITextView.swift index 95a9b19..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 @@ -14,8 +15,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..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 @@ -17,13 +18,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..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 @@ -14,36 +15,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 triggerForSelector(selector: Selector) -> Signal<(), NoError> { + + private func trigger(for 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. /// It mimics the same input as `dismissViewControllerAnimated`: a `Bool` flag for the animation /// and a `(Void -> Void)?` closure for `completion`. @@ -55,21 +56,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 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 } } - + return property } } diff --git a/Tests/ActionTests.swift b/Tests/ActionTests.swift index abd4d1c..87f4135 100644 --- a/Tests/ActionTests.swift +++ b/Tests/ActionTests.swift @@ -7,14 +7,15 @@ // @testable import Rex +import ReactiveSwift import ReactiveCocoa import XCTest import enum Result.NoError final class ActionTests: XCTestCase { - enum TestError: ErrorType { - case Unknown + enum TestError: Error { + case unknown } func testStarted() { @@ -33,7 +34,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 +56,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 +70,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..e04158f 100644 --- a/Tests/Foundation/NSObjectTests.swift +++ b/Tests/Foundation/NSObjectTests.swift @@ -7,6 +7,7 @@ // import Rex +import ReactiveSwift import ReactiveCocoa import XCTest @@ -16,7 +17,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" @@ -25,13 +26,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(description: "Expected timer to send `completed` event when object deallocates") + defer { self.waitForExpectations(timeout: 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() } @@ -59,7 +60,7 @@ final class NSObjectDeallocTests: XCTestCase { let object = Object() _object = object - associatedProperty(object, keyPath: "string", placeholder: { _ in "" }) <~ SignalProducer(value: "Test") + associatedProperty(object, keyPath: "string", placeholder: { _ in "" as NSString }) <~ SignalProducer(value: "Test") XCTAssert(_object?.string == "Test") } } diff --git a/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift b/Tests/Helpers/UIControl+EnableSendActionsForControlEvents.swift index 24d5db1..2df3f0a 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 @@ -15,43 +39,17 @@ import UIKit /// the pair target+action. extension UIControl { - public override class func initialize() { - - struct Static { - static var token: dispatch_once_t = 0 - } - + override open class func initialize() { 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/NSControlTests.swift b/Tests/NSControlTests.swift new file mode 100644 index 0000000..c51eec1 --- /dev/null +++ b/Tests/NSControlTests.swift @@ -0,0 +1,38 @@ +// +// NSControlTests.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveSwift +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: .zero) + _control = control + + control.rex_enabled <~ SignalProducer(value: false) + XCTAssert(_control?.isEnabled == false) + } + + func testAlphaValuePropertyDoesntCreateRetainCycle() { + let control = NSControl(frame: .zero) + _control = control + + control.rex_alphaValue <~ SignalProducer(value: 0.0) + XCTAssert(_control?.alphaValue == 0.0) + } +} diff --git a/Tests/NSTextFieldTests.swift b/Tests/NSTextFieldTests.swift new file mode 100644 index 0000000..3a57838 --- /dev/null +++ b/Tests/NSTextFieldTests.swift @@ -0,0 +1,30 @@ +// +// NSTextFieldTests.swift +// Rex +// +// Created by Christopher Liscio on 1/7/16. +// Copyright © 2016 Neil Pankey. All rights reserved. +// + +@testable import Rex +import ReactiveSwift +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: .zero) + _textField = label + + label.rex_textColor <~ SignalProducer(value: NSColor.green) + XCTAssert(_textField?.textColor == NSColor.green) + } +} diff --git a/Tests/PropertyTests.swift b/Tests/PropertyTests.swift index ea32ccb..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 @@ -32,7 +33,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(Property(initial: false, then: signal)) and2.producer.startWithNext { current = $0 } XCTAssertFalse(and2.value) @@ -62,7 +63,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(Property(initial: true, then: signal)) or2.producer.startWithNext { current = $0 } XCTAssertTrue(or2.value) diff --git a/Tests/SignalProducerTests.swift b/Tests/SignalProducerTests.swift index 35ef801..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 @@ -14,7 +15,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 +25,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 +65,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 +75,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 +89,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 +99,7 @@ final class SignalProducerTests: XCTestCase { var value = -1 var completed = false producer - .deferredRetry(1, onScheduler: scheduler) + .deferredRetry(1, on: scheduler) .start(Observer( next: { value = $0 }, completed: { completed = true } @@ -110,12 +113,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 +130,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(1, on: scheduler, count: 2) .start(Observer( next: { value = $0 }, failed: { _ in failed = true } @@ -148,12 +151,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..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 @@ -47,7 +48,7 @@ final class SignalTests: XCTestCase { observer.sendNext(1) XCTAssertFalse(completed) - observer.sendFailed(.Default) + observer.sendFailed(.default) XCTAssertTrue(completed) } @@ -56,13 +57,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 +74,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 +97,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) @@ -136,7 +137,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) } @@ -152,7 +153,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) } @@ -168,7 +169,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 } @@ -179,13 +180,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: Error { + case `default` } diff --git a/Tests/UIKit/UIActivityIndicatorViewTests.swift b/Tests/UIKit/UIActivityIndicatorViewTests.swift index d47d1cb..67e7edd 100644 --- a/Tests/UIKit/UIActivityIndicatorViewTests.swift +++ b/Tests/UIKit/UIActivityIndicatorViewTests.swift @@ -7,6 +7,7 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa import Result @@ -20,15 +21,15 @@ class UIActivityIndicatorTests: XCTestCase { } func testAnimatingProperty() { - let indicatorView = UIActivityIndicatorView(frame: CGRectZero) + let indicatorView = UIActivityIndicatorView(frame: CGRect.zero) _activityIndicatorView = indicatorView let (pipeSignal, observer) = Signal.pipe() 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/UIBarButtonItemTests.swift b/Tests/UIKit/UIBarButtonItemTests.swift index 8b65c36..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 @@ -32,15 +33,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..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 @@ -13,12 +14,8 @@ import enum Result.NoError extension UIButton { static func button() -> UIButton { - 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) + let button = UIButton(type: UIButtonType.custom) + return button } } @@ -32,15 +29,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,49 +47,50 @@ 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 pressed = MutableProperty(false) let action = Action<(), Bool, NoError> { _ in SignalProducer(value: true) } - passed <~ SignalProducer(signal: action.values) + pressed <~ SignalProducer(signal: action.values) button.rex_pressed <~ SignalProducer(value: CocoaAction(action, input: ())) - - button.sendActionsForControlEvents(.TouchUpInside) - - - XCTAssertTrue(passed.value) + + XCTAssertFalse(pressed.value) + + button.sendActions(for: .touchUpInside) + + XCTAssertTrue(pressed.value) } } diff --git a/Tests/UIKit/UICollectionReusableViewTests.swift b/Tests/UIKit/UICollectionReusableViewTests.swift index b95afac..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() { @@ -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..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 @@ -21,72 +22,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 +96,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..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 @@ -13,15 +14,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 +32,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(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) @@ -40,8 +41,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..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 @@ -21,7 +22,7 @@ class UIImageViewTests: XCTestCase { } func testImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) _imageView = imageView let image = UIImage() @@ -31,7 +32,7 @@ class UIImageViewTests: XCTestCase { } func testHighlightedImagePropertyDoesntCreateRetainCycle() { - let imageView = UIImageView(frame: CGRectZero) + let imageView = UIImageView(frame: CGRect.zero) _imageView = imageView let image = UIImage() @@ -41,7 +42,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 +57,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..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 @@ -21,7 +22,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 +33,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,7 +48,7 @@ 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")) @@ -58,7 +59,7 @@ class UILabelTests: XCTestCase { let firstChange = NSAttributedString(string: "first") let secondChange = NSAttributedString(string: "second") - let label = UILabel(frame: CGRectZero) + let label = UILabel(frame: CGRect.zero) label.attributedText = NSAttributedString(string: "") let (pipeSignal, observer) = Signal.pipe() @@ -71,13 +72,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..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 @@ -20,7 +21,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 +32,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/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 9391052..684914e 100644 --- a/Tests/UIKit/UISwitchTests.swift +++ b/Tests/UIKit/UISwitchTests.swift @@ -7,25 +7,26 @@ // import XCTest +import ReactiveSwift import ReactiveCocoa 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..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() { @@ -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..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() { @@ -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..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 @@ -13,10 +14,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(description: "Expected `rex_text`'s value to equal to the textField's text") + defer { self.waitForExpectations(timeout: 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,9 +26,9 @@ class UITextFieldTests: XCTestCase { } #if os(iOS) - textField.sendActionsForControlEvents(.EditingChanged) + textField.sendActions(for: .editingChanged) #else - NSNotificationCenter.defaultCenter().postNotificationName(UITextFieldTextDidChangeNotification, object: textField) + NotificationCenter.default.post(name: .UITextFieldTextDidChange, object: textField) #endif } } diff --git a/Tests/UIKit/UITextViewTests.swift b/Tests/UIKit/UITextViewTests.swift index 2ae4fc5..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 @@ -13,10 +14,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(description: "Expected `rex_text`'s value to equal to the textViews's text") + defer { self.waitForExpectations(timeout: 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 +25,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..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 @@ -22,8 +23,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(description: "Expected rex_viewDidDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -37,8 +38,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(description: "Expected rex_viewWillDisappear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -52,8 +53,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(description: "Expected rex_viewDidAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -67,8 +68,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(description: "Expected rex_viewWillAppear to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -82,8 +83,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(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -97,8 +98,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(description: "Expected rex_dismissModally to be triggered") + defer { self.waitForExpectations(timeout: 2, handler: nil) } let viewController = UIViewController() _viewController = viewController @@ -107,6 +108,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..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 @@ -21,7 +22,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 +30,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 +67,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) } }